view_component_reflex 3.1.10 → 3.1.11

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: 16b2b912f26f5a3ac4cb26e659d443ce52bca8458154e4cc464ea29d484e9501
4
- data.tar.gz: 5b320e62171c3ab7e436f39db45c98c52f7274b8f477a6f9ee11af13ac2b1899
3
+ metadata.gz: 447c5a4a1ce73910929aa5aca37f8e09ae0469fc80aa042057dbf91e50d8b756
4
+ data.tar.gz: a6a47cb68cc385fd1d4de4b06a895b94a86ef7f624c796bc883cbb2c43716d46
5
5
  SHA512:
6
- metadata.gz: 22d9b03168e43de149a861420b8b819be452ffeb474b8b700cc74af37115cc98849284f2723c920408db60acc957121bf519a1430f65b4efeda503494ccd511f
7
- data.tar.gz: b0dee0a65bdcf956cebaac0220c615f8fcdbd15d3ecbdeb147129a76612a00556813940416da1938d2f2f4c686898855e8bc5c861eda3441e5fddd4442fe6d17
6
+ metadata.gz: 00e76451e67b57889f752384a1abc06251cdd62265af723a2474a25542390a7572355a3de90db866cdde2fbdb5921501acfbc335629134995cb191d8f5c75560
7
+ data.tar.gz: bdb1220f50503c1d83811545a5694f50eff9fb9013de619138c9ff1c26e9906d275ff98fb65b7b7dc0a8a4f4d6fe963baf535da40e87c907091cec497985d18c
data/README.md CHANGED
@@ -1,412 +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
- ### 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
-
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
+