view_component_reflex 3.0.6.pre.1 → 3.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aa816c40dd5218d794d19e5b41cc2328d0ed9b3f10e03f8e507001cacb96c627
4
- data.tar.gz: ef1ad23aa5e1fae0cf0a8ee3a87af8a5d9179ee5f729554288c7a6811edfbdec
3
+ metadata.gz: e1c5a6bb6d424caf8b1840123432a3c50262739780a9c653c73d3bb4d7154906
4
+ data.tar.gz: 9314ec969dd8c0d31ac5751a40a8fa58cdc1f88fac3139775803b15a6284ae30
5
5
  SHA512:
6
- metadata.gz: 7ad4225e776e59d3fdeed3c7a37e59a614fabc1f4ef0c0982a8008d885d03faab21cefee30c3c5fb618a9dec57c3406525d405c62365056d2d04f368b88d6978
7
- data.tar.gz: 67ae62aa96d3ee3ac4c309207434fc3324dc6d1d62f7e09561a0baad5bff497480be5c1b83c256451cabc617a82674242b25adf9074bcedbf8107d85954b0fa3
6
+ metadata.gz: f2b2dbeca7e309d7611271a81d39bbda1d62c8d4680c81376ad149818bb6b866e5cf8c2d97c378ed5ca2b31d6e1dc05bd2accacc4e56f6d46463ef24c53f95d1
7
+ data.tar.gz: a24726fe210f24efe596f82931b74d99fb60dcd89e7c3ce4d4b71dbc8ca6974018bd92c954b3e6942ca6e5b282e469458be8eeaf2de2f978d856fe654e10c5d4
data/README.md CHANGED
@@ -1,401 +1,412 @@
1
- # ViewComponentReflex
2
-
3
- ViewComponentReflex allows you to write reflexes right in your view component code.
4
-
5
- It builds upon [stimulus_reflex](https://github.com/hopsoft/stimulus_reflex) and [view_component](https://github.com/github/view_component)
6
-
7
- ## Usage
8
-
9
- You can add reflexes to your component by inheriting from `ViewComponentReflex::Component`.
10
-
11
- This will act as if you created a reflex with the method `my_cool_stuff`. To call this reflex, add `data-reflex="click->MyComponentReflex#my_cool_reflex"`, just like you're
12
- using stimulus reflex.
13
-
14
- ViewComponentReflex will maintain your component's instance variables between renders. You need to include `data-key=<%= key %>` on your root element, as well
15
- as any element that stimulates a reflex. ViewComponent is inherently state-less, so the key is used to reconcile state to its respective component.
16
-
17
- ### Example
18
- ```ruby
19
- # counter_component.rb
20
- class CounterComponent < ViewComponentReflex::Component
21
- def initialize
22
- @count = 0
23
- end
24
-
25
- def increment
26
- @count += 1
27
- end
28
- end
29
- ```
30
-
31
- ```erb
32
- # counter_component.html.erb
33
- <%= component_controller do %>
34
- <p><%= @count %></p>
35
- <%= reflex_tag :increment, :button, "Click" %>
36
- <% end %>
37
- ```
38
-
39
- ## Collections
40
-
41
- In order to reconcile state to components in collections, you can specify a `collection_key` method that returns some
42
- value unique to that component.
43
-
44
- ```ruby
45
- class TodoComponent < ViewComponentReflex::Component
46
- def initialize(todo:)
47
- @todo = todo
48
- end
49
-
50
- def collection_key
51
- @todo.id
52
- end
53
- end
54
- #
55
- <%= render(TodoComponent.with_collection(Todo.all)) %>
56
- ```
57
-
58
- ## API
59
-
60
- ### permit_parameter?(initial_param, new_params)
61
- If a new parameter is passed to the component during rendering, it is used instead of what's in state.
62
- If you're storing instances in state, you can use this to properly compare them.
63
-
64
- ```ruby
65
- def permit_parameter?(initial_param, new_param)
66
- if new_param.instance_of? MyModel
67
- new_param.id == @my_model.id
68
- else
69
- super
70
- end
71
- end
72
- ```
73
-
74
- ### omitted_from_state
75
- Return an array of instance variables you want to omit from state. Only really useful if you're using the session state
76
- adapter, and you have an instance variable that can't be serialized.
77
-
78
- ```ruby
79
- def omitted_from_state
80
- [:@form]
81
- end
82
- ```
83
-
84
- ### reflex_tag(reflex, name, content_or_options_with_block = nil, options = nil, escape = true, &block)
85
- This shares the same definition as `content_tag`, except it accepts a reflex as the first parameter.
86
-
87
- ```erb
88
- <%= reflex_tag :increment, :button, "Click me!" %>
89
- ```
90
-
91
- Would add a click handler to the `increment` method on your component.
92
-
93
- To use a non-click event, specific that with `->` notation
94
-
95
- ```erb
96
- <%= reflex_tag "mouseenter->increment", :button, "Click me!" %>
97
- ```
98
-
99
- ### reflex_data_attributes(reflex)
100
-
101
- This helper will give you the data attributes used in the reflex_tag above if you want to build your own elements.
102
-
103
- Build your own tag:
104
-
105
- ```erb
106
- <%= link_to (image_tag photo.image.url(:medium)), data: reflex_data_attributes(:increment) %>
107
- ```
108
-
109
- Render a ViewComponent
110
-
111
- ```erb
112
- <%= render ButtonComponent.new(data: reflex_data_attributes("mouseenter->increment")) %>
113
- ```
114
-
115
- Make sure that you assign the reflex_data_attributes to the correct element in your component.
116
-
117
- ### collection_key
118
- If you're rendering a component as a collection with `MyComponent.with_collection(SomeCollection)`, you must define this method to return some unique value for the component.
119
- This is used to reconcile state in the background.
120
-
121
- ```ruby
122
- def initialize
123
- @my_model = MyModel.new
124
- end
125
-
126
- def collection_key
127
- @my_model.id
128
- end
129
- ```
130
-
131
- ### stimulate(target, data)
132
- Stimulate another reflex from within your component. This typically requires the key of the component you're stimulating
133
- which can be passed in via parameters.
134
-
135
- ```ruby
136
- def initialize(parent_key)
137
- @parent_key = parent_key
138
- end
139
-
140
- def stimulate_other
141
- stimulate("OtherComponent#method", { key: @parent_key })
142
- end
143
- ```
144
-
145
- ### refresh!(selectors)
146
- Refresh a specific element on the page. Using this will implicitly run `prevent_render!`.
147
- If you want to render a specific element, as well as the component, a common pattern would be to pass `selector` as one of the parameters
148
-
149
- ```
150
- def my_method
151
- refresh! '#my-special-element', selector
152
- end
153
- ```
154
-
155
- ### selector
156
- Returns the unique selector for this component. Useful to pass to `refresh!` when refreshing custom elements.
157
-
158
- ### prevent_refresh!
159
- By default, VCR will re-render your component after it executes your method. `prevent_refresh!` prevents this from happening.
160
-
161
- ```ruby
162
- def my_method
163
- prevent_refresh!
164
- @foo = :bar
165
- end # the rendered page will not reflect this change
166
- ```
167
-
168
- ### refresh_all!
169
- Refresh the entire body of the page
170
-
171
- ```ruby
172
- def do_some_global_action
173
- prevent_refresh!
174
- session[:model] = MyModel.new
175
- refresh_all!
176
- end
177
- ```
178
-
179
- ### key
180
- This is a key unique to a particular component. It's used to reconcile state between renders, and should be passed as a data attribute whenever a reflex is called
181
-
182
- ```erb
183
- <button type="button" data-reflex="click->MyComponent#do_something" data-key="<%= key %>">Click me!</button>
184
- ```
185
-
186
- ### component_controller(options = {}, &blk)
187
- This is a view helper to properly connect VCR to the component. It outputs `<div data-controller="my-controller" key=<%= key %></div>`
188
- You *must* wrap your component in this for everything to work properly.
189
-
190
- ```erb
191
- <%= component_controller do %>
192
- <p><%= @count %></p
193
- <% end %>
194
- ```
195
-
196
- ### after_state_initialized(parameters_changed)
197
-
198
- This is called after the state has been inserted in the component. You can use this to run conditional functions after
199
- some parameter has superseeded whatever's in state
200
-
201
- ```
202
- def after_state_initialized(parameters_changed)
203
- if parameters_changed.include?(:@filter)
204
- calculate_visible_rows
205
- end
206
- end
207
- ```
208
-
209
- ## Custom reflex base class
210
- Reflexes typically inherit from a base ApplicationReflex. You can define the base class for a view_component_reflex by using the `reflex_base_class` accessor.
211
- The parent class must inherit ViewComponentReflex::Reflex, and will throw an error if it does not.
212
-
213
- ```ruby
214
- class ApplicationReflex < ViewComponentReflex::Reflex
215
-
216
- end
217
-
218
-
219
- class MyComponent < ViewComponentReflex::Component
220
- MyComponent.reflex_base_class = ApplicationReflex
221
- end
222
- ```
223
-
224
- ## Common patterns
225
- A lot of the time, you only need to update specific components when changing instance variables. For example, changing `@loading` might only need
226
- to display a spinner somewhere on the page. You can define setters to implicitly render the appropriate pieces of dom whenever that variable is set
227
-
228
- ```ruby
229
- def initialize
230
- @loading = false
231
- end
232
-
233
- def loading=(new_value)
234
- @loading = new_value
235
- refresh! '#loader'
236
- end
237
-
238
- def do_expensive_action
239
- prevent_refresh!
240
-
241
- self.loading = true
242
- execute_it
243
- self.loading = false
244
- end
245
- ```
246
-
247
- ```erb
248
- <%= component_controller do %>
249
- <div id="loader">
250
- <% if @loading %>
251
- <p>Loading...</p>
252
- <% end %>
253
- </div>
254
-
255
- <button type="button" data-reflex="click->MyComponent#do_expensive_action" data-key="<%= key %>">Click me!</button>
256
- <% end
257
- ```
258
-
259
- ## State
260
-
261
- By default (since version `2.3.2`), view_component_reflex stores component state in session. You can optionally set the state adapter
262
- to use the memory by changing `config.state_adapter` to `ViewComponentReflex::StateAdapter::Memory`.
263
-
264
- ## Custom State Adapters
265
-
266
- ViewComponentReflex uses session for its state by default. To change this, add
267
- an initializer to `config/initializers/view_component_reflex.rb`.
268
-
269
- ```ruby
270
- ViewComponentReflex::Engine.configure do |config|
271
- config.state_adapter = YourAdapter
272
- end
273
- ```
274
-
275
-
276
- ## Existing Fast Redis based State Adapter
277
-
278
- This adapter uses hmset and hgetall to reduce the number of operations.
279
- This is the recommended adapter if you are using AnyCable.
280
-
281
- ```ruby
282
- ViewComponentReflex::Engine.configure do |config|
283
- config.state_adapter = ViewComponentReflex::StateAdapter::Redis.new(
284
- redis_opts: {
285
- url: "redis://localhost:6379/1", driver: :hiredis
286
- },
287
- ttl: 3600)
288
- end
289
- ```
290
-
291
- `YourAdapter` should implement
292
-
293
- ```ruby
294
- class YourAdapter
295
- ##
296
- # request - a rails request object
297
- # key - a unique string that identifies the component instance
298
- def self.state(request, key)
299
- # Return state for a given key
300
- end
301
-
302
- ##
303
- # set_state is used to modify the state.
304
- #
305
- # request - a rails request object
306
- # controller - the current controller
307
- # key - a unique string that identifies the component
308
- # new_state - the new state to set
309
- def self.set_state(request, controller, key, new_state)
310
- # update the state
311
- end
312
-
313
-
314
- ##
315
- # store_state is used to replace the state entirely. It only accepts
316
- # a request object, rather than a reflex because it's called from the component's
317
- # side with the component's instance variables.
318
- #
319
- # request - a rails request object
320
- # key - a unique string that identifies the component instance
321
- # new_state - a hash containing the component state
322
- def self.store_state(request, key, new_state = {})
323
- # replace the state
324
- end
325
- end
326
- ```
327
-
328
-
329
- ## Installation
330
- Add this line to your application's Gemfile:
331
-
332
- ```ruby
333
- gem 'view_component_reflex'
334
- ```
335
-
336
- And then execute:
337
- ```bash
338
- $ bundle
339
- ```
340
-
341
- Or install it yourself as:
342
- ```bash
343
- $ gem install view_component_reflex
344
- ```
345
-
346
- # Common problems
347
-
348
- ## Uninitialized constants \<component\>Reflex
349
- A component needs to be wrapped in `<%= component_controller do %>` in order to properly initialize, otherwise the Reflex class won't get created.
350
-
351
- ## Session is an empty hash
352
- StimulusReflex 3.3 introduced _selector morphs_, allowing you to render arbitrary strings via `ApplicationController.render`, for example:
353
-
354
- ```rb
355
- def test_selector
356
- morph '#some-container', ApplicationController.render(MyComponent.new(some: :param))
357
- end
358
- ```
359
-
360
- StimulusReflex 3.4 introduced a fix that merges the current `request.env` and provides the CSRF token to fetch the session.
361
-
362
- ## Help, my instance variables do not persist into the session
363
-
364
- These instance variable names are not working and unsafe:
365
-
366
- ```rb
367
- def unsafe_instance_variables
368
- [
369
- :@view_context, :@lookup_context, :@view_renderer, :@view_flow,
370
- :@virtual_path, :@variant, :@current_template, :@output_buffer, :@key,
371
- :@helpers, :@controller, :@request, :@tag_builder, :@initialized_state
372
- ]
373
- end
374
- ```
375
- Please use a different name to be able to save them to the session.
376
-
377
- ## Anycable
378
-
379
- @sebyx07 provided a solution to use anycable (https://github.com/joshleblanc/view_component_reflex/issues/23#issue-721786338)
380
-
381
- Leaving this, might help others:
382
-
383
- I tried this with any cable and I had to add this to development.rb
384
- Otherwise @instance_variables were nil after a reflex
385
-
386
- ```ruby
387
- config.cache_store = :redis_cache_store, { url: "redis://localhost:6379/1", driver: :hiredis }
388
- config.session_store :cache_store
389
- ```
390
-
391
- ## License
392
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
393
-
394
- ## Caveats
395
-
396
- State uses session to maintain state as of right now. It also assumes your component view is written with a file extension of either `.html.erb`, `.html.haml` or `.html.slim`.
397
-
398
- ## Support me
399
-
400
- <a href="https://www.buymeacoffee.com/jleblanc" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="40" ></a>
401
-
1
+ # ViewComponentReflex
2
+
3
+ ViewComponentReflex allows you to write reflexes right in your view component code.
4
+
5
+ It builds upon [stimulus_reflex](https://github.com/hopsoft/stimulus_reflex) and [view_component](https://github.com/github/view_component)
6
+
7
+ ## Usage
8
+
9
+ You can add reflexes to your component by inheriting from `ViewComponentReflex::Component`.
10
+
11
+ This will act as if you created a reflex with the method `my_cool_stuff`. To call this reflex, add `data-reflex="click->MyComponentReflex#my_cool_reflex"`, just like you're
12
+ using stimulus reflex.
13
+
14
+ ViewComponentReflex will maintain your component's instance variables between renders. You need to include `data-key=<%= key %>` on your root element, as well
15
+ as any element that stimulates a reflex. ViewComponent is inherently state-less, so the key is used to reconcile state to its respective component.
16
+
17
+ ### Example
18
+ ```ruby
19
+ # counter_component.rb
20
+ class CounterComponent < ViewComponentReflex::Component
21
+ def initialize
22
+ @count = 0
23
+ end
24
+
25
+ def increment
26
+ @count += 1
27
+ end
28
+ end
29
+ ```
30
+
31
+ ```erb
32
+ # counter_component.html.erb
33
+ <%= component_controller do %>
34
+ <p><%= @count %></p>
35
+ <%= reflex_tag :increment, :button, "Click" %>
36
+ <% end %>
37
+ ```
38
+
39
+ ## Collections
40
+
41
+ In order to reconcile state to components in collections, you can specify a `collection_key` method that returns some
42
+ value unique to that component.
43
+
44
+ ```ruby
45
+ class TodoComponent < ViewComponentReflex::Component
46
+ def initialize(todo:)
47
+ @todo = todo
48
+ end
49
+
50
+ def collection_key
51
+ @todo.id
52
+ end
53
+ end
54
+ #
55
+ <%= render(TodoComponent.with_collection(Todo.all)) %>
56
+ ```
57
+
58
+ ## API
59
+
60
+ ### permit_parameter?(initial_param, new_params)
61
+ If a new parameter is passed to the component during rendering, it is used instead of what's in state.
62
+ If you're storing instances in state, you can use this to properly compare them.
63
+
64
+ ```ruby
65
+ def permit_parameter?(initial_param, new_param)
66
+ if new_param.instance_of? MyModel
67
+ new_param.id == @my_model.id
68
+ else
69
+ super
70
+ end
71
+ end
72
+ ```
73
+
74
+ ### omitted_from_state
75
+ Return an array of instance variables you want to omit from state. Only really useful if you're using the session state
76
+ adapter, and you have an instance variable that can't be serialized.
77
+
78
+ ```ruby
79
+ def omitted_from_state
80
+ [:@form]
81
+ end
82
+ ```
83
+
84
+ ### reflex_tag(reflex, name, content_or_options_with_block = nil, options = nil, escape = true, &block)
85
+ This shares the same definition as `content_tag`, except it accepts a reflex as the first parameter.
86
+
87
+ ```erb
88
+ <%= reflex_tag :increment, :button, "Click me!" %>
89
+ ```
90
+
91
+ Would add a click handler to the `increment` method on your component.
92
+
93
+ To use a non-click event, specific that with `->` notation
94
+
95
+ ```erb
96
+ <%= reflex_tag "mouseenter->increment", :button, "Click me!" %>
97
+ ```
98
+
99
+ ### reflex_data_attributes(reflex)
100
+
101
+ This helper will give you the data attributes used in the reflex_tag above if you want to build your own elements.
102
+
103
+ Build your own tag:
104
+
105
+ ```erb
106
+ <%= link_to (image_tag photo.image.url(:medium)), data: reflex_data_attributes(:increment) %>
107
+ ```
108
+
109
+ Render a ViewComponent
110
+
111
+ ```erb
112
+ <%= render ButtonComponent.new(data: reflex_data_attributes("mouseenter->increment")) %>
113
+ ```
114
+
115
+ Make sure that you assign the reflex_data_attributes to the correct element in your component.
116
+
117
+ ### collection_key
118
+ If you're rendering a component as a collection with `MyComponent.with_collection(SomeCollection)`, you must define this method to return some unique value for the component.
119
+ This is used to reconcile state in the background.
120
+
121
+ ```ruby
122
+ def initialize
123
+ @my_model = MyModel.new
124
+ end
125
+
126
+ def collection_key
127
+ @my_model.id
128
+ end
129
+ ```
130
+
131
+ ### stimulate(target, data)
132
+ Stimulate another reflex from within your component. This typically requires the key of the component you're stimulating
133
+ which can be passed in via parameters.
134
+
135
+ ```ruby
136
+ def initialize(parent_key)
137
+ @parent_key = parent_key
138
+ end
139
+
140
+ def stimulate_other
141
+ stimulate("OtherComponent#method", { key: @parent_key })
142
+ end
143
+ ```
144
+
145
+ ### refresh!(selectors)
146
+ Refresh a specific element on the page. Using this will implicitly run `prevent_render!`.
147
+ If you want to render a specific element, as well as the component, a common pattern would be to pass `selector` as one of the parameters
148
+
149
+ ```
150
+ def my_method
151
+ refresh! '#my-special-element', selector
152
+ end
153
+ ```
154
+
155
+ ### selector
156
+ Returns the unique selector for this component. Useful to pass to `refresh!` when refreshing custom elements.
157
+
158
+ ### prevent_refresh!
159
+ By default, VCR will re-render your component after it executes your method. `prevent_refresh!` prevents this from happening.
160
+
161
+ ```ruby
162
+ def my_method
163
+ prevent_refresh!
164
+ @foo = :bar
165
+ end # the rendered page will not reflect this change
166
+ ```
167
+
168
+ ### refresh_all!
169
+ Refresh the entire body of the page
170
+
171
+ ```ruby
172
+ def do_some_global_action
173
+ prevent_refresh!
174
+ session[:model] = MyModel.new
175
+ refresh_all!
176
+ end
177
+ ```
178
+
179
+ ### stream_to(channel)
180
+ Stream to a custom channel, rather than the default stimulus reflex one
181
+
182
+ ```ruby
183
+ def do_something
184
+ stream_to MyChannel
185
+
186
+ @foo = :bar
187
+ end
188
+ ```
189
+
190
+ ### key
191
+ This is a key unique to a particular component. It's used to reconcile state between renders, and should be passed as a data attribute whenever a reflex is called
192
+
193
+ ```erb
194
+ <button type="button" data-reflex="click->MyComponent#do_something" data-key="<%= key %>">Click me!</button>
195
+ ```
196
+
197
+ ### component_controller(options = {}, &blk)
198
+ This is a view helper to properly connect VCR to the component. It outputs `<div data-controller="my-controller" key=<%= key %></div>`
199
+ You *must* wrap your component in this for everything to work properly.
200
+
201
+ ```erb
202
+ <%= component_controller do %>
203
+ <p><%= @count %></p
204
+ <% end %>
205
+ ```
206
+
207
+ ### after_state_initialized(parameters_changed)
208
+
209
+ This is called after the state has been inserted in the component. You can use this to run conditional functions after
210
+ some parameter has superseeded whatever's in state
211
+
212
+ ```
213
+ def after_state_initialized(parameters_changed)
214
+ if parameters_changed.include?(:@filter)
215
+ calculate_visible_rows
216
+ end
217
+ end
218
+ ```
219
+
220
+ ## Custom reflex base class
221
+ Reflexes typically inherit from a base ApplicationReflex. You can define the base class for a view_component_reflex by using the `reflex_base_class` accessor.
222
+ The parent class must inherit ViewComponentReflex::Reflex, and will throw an error if it does not.
223
+
224
+ ```ruby
225
+ class ApplicationReflex < ViewComponentReflex::Reflex
226
+
227
+ end
228
+
229
+
230
+ class MyComponent < ViewComponentReflex::Component
231
+ MyComponent.reflex_base_class = ApplicationReflex
232
+ end
233
+ ```
234
+
235
+ ## Common patterns
236
+ A lot of the time, you only need to update specific components when changing instance variables. For example, changing `@loading` might only need
237
+ to display a spinner somewhere on the page. You can define setters to implicitly render the appropriate pieces of dom whenever that variable is set
238
+
239
+ ```ruby
240
+ def initialize
241
+ @loading = false
242
+ end
243
+
244
+ def loading=(new_value)
245
+ @loading = new_value
246
+ refresh! '#loader'
247
+ end
248
+
249
+ def do_expensive_action
250
+ prevent_refresh!
251
+
252
+ self.loading = true
253
+ execute_it
254
+ self.loading = false
255
+ end
256
+ ```
257
+
258
+ ```erb
259
+ <%= component_controller do %>
260
+ <div id="loader">
261
+ <% if @loading %>
262
+ <p>Loading...</p>
263
+ <% end %>
264
+ </div>
265
+
266
+ <button type="button" data-reflex="click->MyComponent#do_expensive_action" data-key="<%= key %>">Click me!</button>
267
+ <% end
268
+ ```
269
+
270
+ ## State
271
+
272
+ By default (since version `2.3.2`), view_component_reflex stores component state in session. You can optionally set the state adapter
273
+ to use the memory by changing `config.state_adapter` to `ViewComponentReflex::StateAdapter::Memory`.
274
+
275
+ ## Custom State Adapters
276
+
277
+ ViewComponentReflex uses session for its state by default. To change this, add
278
+ an initializer to `config/initializers/view_component_reflex.rb`.
279
+
280
+ ```ruby
281
+ ViewComponentReflex::Engine.configure do |config|
282
+ config.state_adapter = YourAdapter
283
+ end
284
+ ```
285
+
286
+
287
+ ## Existing Fast Redis based State Adapter
288
+
289
+ This adapter uses hmset and hgetall to reduce the number of operations.
290
+ This is the recommended adapter if you are using AnyCable.
291
+
292
+ ```ruby
293
+ ViewComponentReflex::Engine.configure do |config|
294
+ config.state_adapter = ViewComponentReflex::StateAdapter::Redis.new(
295
+ redis_opts: {
296
+ url: "redis://localhost:6379/1", driver: :hiredis
297
+ },
298
+ ttl: 3600)
299
+ end
300
+ ```
301
+
302
+ `YourAdapter` should implement
303
+
304
+ ```ruby
305
+ class YourAdapter
306
+ ##
307
+ # request - a rails request object
308
+ # key - a unique string that identifies the component instance
309
+ def self.state(request, key)
310
+ # Return state for a given key
311
+ end
312
+
313
+ ##
314
+ # set_state is used to modify the state.
315
+ #
316
+ # request - a rails request object
317
+ # controller - the current controller
318
+ # key - a unique string that identifies the component
319
+ # new_state - the new state to set
320
+ def self.set_state(request, controller, key, new_state)
321
+ # update the state
322
+ end
323
+
324
+
325
+ ##
326
+ # store_state is used to replace the state entirely. It only accepts
327
+ # a request object, rather than a reflex because it's called from the component's
328
+ # side with the component's instance variables.
329
+ #
330
+ # request - a rails request object
331
+ # key - a unique string that identifies the component instance
332
+ # new_state - a hash containing the component state
333
+ def self.store_state(request, key, new_state = {})
334
+ # replace the state
335
+ end
336
+ end
337
+ ```
338
+
339
+
340
+ ## Installation
341
+ Add this line to your application's Gemfile:
342
+
343
+ ```ruby
344
+ gem 'view_component_reflex'
345
+ ```
346
+
347
+ And then execute:
348
+ ```bash
349
+ $ bundle
350
+ ```
351
+
352
+ Or install it yourself as:
353
+ ```bash
354
+ $ gem install view_component_reflex
355
+ ```
356
+
357
+ # Common problems
358
+
359
+ ## Uninitialized constants \<component\>Reflex
360
+ A component needs to be wrapped in `<%= component_controller do %>` in order to properly initialize, otherwise the Reflex class won't get created.
361
+
362
+ ## Session is an empty hash
363
+ StimulusReflex 3.3 introduced _selector morphs_, allowing you to render arbitrary strings via `ApplicationController.render`, for example:
364
+
365
+ ```rb
366
+ def test_selector
367
+ morph '#some-container', ApplicationController.render(MyComponent.new(some: :param))
368
+ end
369
+ ```
370
+
371
+ StimulusReflex 3.4 introduced a fix that merges the current `request.env` and provides the CSRF token to fetch the session.
372
+
373
+ ## Help, my instance variables do not persist into the session
374
+
375
+ These instance variable names are not working and unsafe:
376
+
377
+ ```rb
378
+ def unsafe_instance_variables
379
+ [
380
+ :@view_context, :@lookup_context, :@view_renderer, :@view_flow,
381
+ :@virtual_path, :@variant, :@current_template, :@output_buffer, :@key,
382
+ :@helpers, :@controller, :@request, :@tag_builder, :@initialized_state
383
+ ]
384
+ end
385
+ ```
386
+ Please use a different name to be able to save them to the session.
387
+
388
+ ## Anycable
389
+
390
+ @sebyx07 provided a solution to use anycable (https://github.com/joshleblanc/view_component_reflex/issues/23#issue-721786338)
391
+
392
+ Leaving this, might help others:
393
+
394
+ I tried this with any cable and I had to add this to development.rb
395
+ Otherwise @instance_variables were nil after a reflex
396
+
397
+ ```ruby
398
+ config.cache_store = :redis_cache_store, { url: "redis://localhost:6379/1", driver: :hiredis }
399
+ config.session_store :cache_store
400
+ ```
401
+
402
+ ## License
403
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
404
+
405
+ ## Caveats
406
+
407
+ State uses session to maintain state as of right now. It also assumes your component view is written with a file extension of either `.html.erb`, `.html.haml` or `.html.slim`.
408
+
409
+ ## Support me
410
+
411
+ <a href="https://www.buymeacoffee.com/jleblanc" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="40" ></a>
412
+