isomorfeus-react 16.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +1 -0
  3. data/README.md +620 -0
  4. data/isomorfeus-react.gemspec +23 -0
  5. data/lib/isomorfeus-react.rb +131 -0
  6. data/lib/isomorfeus/config.rb +84 -0
  7. data/lib/isomorfeus/top_level.rb +48 -0
  8. data/lib/isomorfeus/view_helpers.rb +38 -0
  9. data/lib/lucid_app/api.rb +22 -0
  10. data/lib/lucid_app/base.rb +7 -0
  11. data/lib/lucid_app/context.rb +7 -0
  12. data/lib/lucid_app/mixin.rb +17 -0
  13. data/lib/lucid_app/native_component_constructor.rb +70 -0
  14. data/lib/lucid_component/api.rb +97 -0
  15. data/lib/lucid_component/base.rb +7 -0
  16. data/lib/lucid_component/event_handler.rb +17 -0
  17. data/lib/lucid_component/initializer.rb +12 -0
  18. data/lib/lucid_component/mixin.rb +17 -0
  19. data/lib/lucid_component/native_component_constructor.rb +131 -0
  20. data/lib/react.rb +147 -0
  21. data/lib/react/active_support_support.rb +13 -0
  22. data/lib/react/component/api.rb +226 -0
  23. data/lib/react/component/base.rb +9 -0
  24. data/lib/react/component/elements.rb +78 -0
  25. data/lib/react/component/event_handler.rb +19 -0
  26. data/lib/react/component/features.rb +47 -0
  27. data/lib/react/component/history.rb +36 -0
  28. data/lib/react/component/initializer.rb +11 -0
  29. data/lib/react/component/location.rb +15 -0
  30. data/lib/react/component/match.rb +31 -0
  31. data/lib/react/component/mixin.rb +19 -0
  32. data/lib/react/component/native_component_constructor.rb +76 -0
  33. data/lib/react/component/native_component_validate_prop.rb +37 -0
  34. data/lib/react/component/props.rb +49 -0
  35. data/lib/react/component/resolution.rb +71 -0
  36. data/lib/react/component/should_component_update.rb +14 -0
  37. data/lib/react/component/state.rb +52 -0
  38. data/lib/react/component/unsafe_api.rb +33 -0
  39. data/lib/react/context_wrapper.rb +47 -0
  40. data/lib/react/function_component/creator.rb +47 -0
  41. data/lib/react/function_component/resolution.rb +61 -0
  42. data/lib/react/function_component/runner.rb +19 -0
  43. data/lib/react/native_constant_wrapper.rb +34 -0
  44. data/lib/react/pure_component/base.rb +9 -0
  45. data/lib/react/pure_component/mixin.rb +17 -0
  46. data/lib/react/redux_component/api.rb +132 -0
  47. data/lib/react/redux_component/app_store_defaults.rb +38 -0
  48. data/lib/react/redux_component/app_store_proxy.rb +46 -0
  49. data/lib/react/redux_component/base.rb +9 -0
  50. data/lib/react/redux_component/class_store_proxy.rb +50 -0
  51. data/lib/react/redux_component/component_class_store_defaults.rb +40 -0
  52. data/lib/react/redux_component/component_instance_store_defaults.rb +41 -0
  53. data/lib/react/redux_component/initializer.rb +14 -0
  54. data/lib/react/redux_component/instance_store_proxy.rb +50 -0
  55. data/lib/react/redux_component/mixin.rb +18 -0
  56. data/lib/react/redux_component/native_component_constructor.rb +119 -0
  57. data/lib/react/redux_component/reducers.rb +53 -0
  58. data/lib/react/ref.rb +19 -0
  59. data/lib/react/synthetic_event.rb +53 -0
  60. data/lib/react/version.rb +3 -0
  61. data/lib/react_dom.rb +31 -0
  62. data/lib/react_dom_server.rb +17 -0
  63. metadata +167 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cba3a632d5700b2370b2dc673eabc01aebac34c508680f79b01154ce856e5051
4
+ data.tar.gz: 4025af5f4037e829aacf72c91316b0da9b312209e92e242a8d8a127d58f1d109
5
+ SHA512:
6
+ metadata.gz: 8b178809f4a89d23283c1b0ccfa85f8df7e720388bf5516f70aa5083e38a7e88777992cfe7c92a53de74f0aa0cb78147ead4e8af11a6d58851c266b09fe862a8
7
+ data.tar.gz: a3da4df7f53d9f86b423750e5166ea214a359290c268d8e800f8254be688843732ce26d29c585dbd8f86f0b7d5580e06bf6afa2b721760024f712b95ccf9c612
data/Gemfile ADDED
@@ -0,0 +1 @@
1
+ gemspec
@@ -0,0 +1,620 @@
1
+ # isomorfeus-react
2
+
3
+ Develop React components for Opal Ruby along with very easy to use and advanced React-Redux Components.
4
+
5
+ ## Versioning
6
+ isomorfeus-react version follows the React version which features and API it implements.
7
+ Isomorfeus-react 16.5.x implements features and the API of React 16.5 and should be used with React 16.5
8
+
9
+ ## Installation
10
+ To install React with the matching version:
11
+ ```
12
+ yarn add react@16.5
13
+ ```
14
+ then add to the Gemfile:
15
+ ```ruby
16
+ gem 'isomorfeus-react' # this will also include isomorfeus-redux
17
+ ```
18
+ then `bundle install`
19
+ and to your client code add:
20
+ ```ruby
21
+ require 'isomorfeus-react' # this will also require isomorfeus-redux
22
+ ```
23
+ ## Usage
24
+ Because isomorfeus-react follows closely the React principles/implementation/API and Documentation, most things of the official React documentation
25
+ apply, but in the Ruby way, see:
26
+ - https://reactjs.org/docs/getting-started.html
27
+
28
+ Redux is also required, for the more advanced components to function properly.
29
+
30
+ React, Redux and accompanying libraries must be imported and made available in the global namespace in the application javascript entry file,
31
+ with webpack this can be ensured by assigning them to the global namespace:
32
+ ```javascript
33
+ import * as Redux from 'redux';
34
+ import React from 'react';
35
+ import ReactDOM from 'react-dom';
36
+ global.Redux = Redux;
37
+ global.React = React;
38
+ global.ReactDOM = ReactDOM;
39
+ ```
40
+
41
+ Following features are presented with its differences to the Javascript React implementation, along with enhancements and the advanced components.
42
+
43
+ ### Class Components
44
+ Class Components can be created in two ways, either by inheritance or by including a module.
45
+ Inheritance:
46
+ ```ruby
47
+ class MyComponent < React::Component::Base
48
+
49
+ end
50
+ ```
51
+ including a module:
52
+ ```ruby
53
+ class MyComponent
54
+ include React::Component::Mixin
55
+
56
+ end
57
+ ```
58
+
59
+ Each Component must have at least a render block:
60
+ ```ruby
61
+ class MyComponent < React::Component::Base
62
+ render do
63
+ DIV { "some text" }
64
+ end
65
+ end
66
+ ```
67
+
68
+ Class Component allow for the definition of a custom should_component_update? block, but that is optional:
69
+ ```ruby
70
+ class MyComponent < React::Component::Base
71
+ should_component_update? do |next_props, next_state|
72
+ return true # to always update for example
73
+ end
74
+
75
+ render do
76
+ DIV { "some text" }
77
+ end
78
+ end
79
+ ```
80
+ A default should_component_update? implementation is supplied. The default should_component_update? implementation for Class Components is most
81
+ efficient if complex props or state are used.
82
+
83
+ **Data flow of a React::Component:**
84
+ ![React::Component Data Flow](https://raw.githubusercontent.com/isomorfeus/isomorfeus-react/master/images/data_flow_component.png)
85
+
86
+
87
+ ### Pure Components
88
+ Pure Components can be created in two ways, either by inheritance or by including a module.
89
+ Inheritance:
90
+ ```ruby
91
+ class MyComponent < React::PureComponent::Base
92
+
93
+ end
94
+ ```
95
+ including a module:
96
+ ```ruby
97
+ class MyComponent
98
+ include React::PureComponent::Mixin
99
+
100
+ end
101
+ ```
102
+
103
+ Each Component must have at least a render block:
104
+ ```ruby
105
+ class MyComponent < React::PureComponent::Base
106
+ render do
107
+ DIV { "some text" }
108
+ end
109
+ end
110
+ ```
111
+
112
+ A PureComponent does not allow for the definition of a custom should_component_update? block. Its using the default React implementation instead.
113
+ Its recommended to use them only if no props or state are used or if props and state have simple values only, like strings or numbers.
114
+
115
+ **Data flow of a React::PureComponent:**
116
+ ![React::PureComponent Data Flow](https://raw.githubusercontent.com/isomorfeus/isomorfeus-react/master/images/data_flow_component.png)
117
+
118
+ ### Function Components
119
+ Function Components are created using a Ruby DSL that is used within the creator class:
120
+ ```ruby
121
+ class React::FunctionComponent::Creator
122
+ function_component 'MyComponent' do |props|
123
+ SPAN { props.text }
124
+ end
125
+ # Javascript .-notation can be used for the component name:
126
+ function_component 'MyObject.MyComponent' do |props|
127
+ SPAN { props.text }
128
+ end
129
+ end
130
+ ```
131
+ This creates a native javascript components.
132
+ The file containing the creator must be explicitly required, because the automatic resolution of Javascript constant names
133
+ is not done by opal-autoloader.
134
+
135
+ A Function Component can then be used in other Components:
136
+ ```ruby
137
+ class MyComponent < React::PureComponent::Base
138
+ render do
139
+ MyComponent(text: 'some text')
140
+ MyObject.MyComponent(text: 'more text')
141
+ end
142
+ end
143
+ ```
144
+ To get the native component, for example to pass it in props, javascript inlining can be used:
145
+ ```ruby
146
+ Route(path: '/fun_fun/:count', exact: true, component: `MyObject.MyComponent`)
147
+ ```
148
+
149
+ **Data flow of a React::FunctionComponent:**
150
+ ![React::FunctionComponent Data Flow](https://raw.githubusercontent.com/isomorfeus/isomorfeus-react/master/images/data_flow_function_component.png)
151
+
152
+ ### Props
153
+ In ruby props are underscored: `className -> class_name`. The conversion for React is done automatically.
154
+ Within a component props can be accessed using `props`:
155
+ ```ruby
156
+ class MyComponent < React::PureComponent::Base
157
+ render do
158
+ DIV { props.text }
159
+ end
160
+ end
161
+ ```
162
+ Props are passed as argument to the component:
163
+ ```ruby
164
+ class MyOtherComponent < React::PureComponent::Base
165
+ render do
166
+ MyComponent(text: 'some other text')
167
+ end
168
+ end
169
+ ```
170
+ Props can be declared and type checked and a default value can be given:
171
+ ```ruby
172
+ class MyComponent < React::PureComponent::Base
173
+ prop :text, class: String # a required prop of class String, class must match exactly
174
+ prop :other, is_a: Enumerable # a required prop, which can be a Array for example, but at least must be a Enumerable
175
+ prop :cool, default: 'yet some more text' # a optional prop with a default value
176
+ prop :even_cooler, class: String, required: false # a optional prop, which when given, must be of class String
177
+
178
+ render do
179
+ DIV { props.text }
180
+ end
181
+ end
182
+ ```
183
+
184
+ ### State
185
+ State can be accessed in components using `state`:
186
+ ```ruby
187
+ class MyComponent < React::PureComponent::Base
188
+ render do
189
+ if state.toggled
190
+ DIV { 'toggled' }
191
+ else
192
+ DIV { 'not toggled' }
193
+ end
194
+ end
195
+ end
196
+ ```
197
+ State can be intialized like so:
198
+ ```ruby
199
+ class MyComponent < React::PureComponent::Base
200
+ state.toggled = false
201
+
202
+ render do
203
+ if state.toggled
204
+ DIV { 'toggled' }
205
+ else
206
+ DIV { 'not toggled' }
207
+ end
208
+ end
209
+ end
210
+ ```
211
+ State can be changed like so, the component setState() will be called:
212
+ ```ruby
213
+ class MyComponent < React::PureComponent::Base
214
+ render do
215
+ if some_condition_is_met
216
+
217
+ state.toggled = true # calls components setState to cause a render
218
+
219
+ # or if a callback is needed:
220
+
221
+ set_state({toggled: true}) do
222
+ # some callback code here
223
+ end
224
+ end
225
+ if state.toggled
226
+ DIV { 'toggled' }
227
+ else
228
+ DIV { 'not toggled' }
229
+ end
230
+ end
231
+ end
232
+ ```
233
+ When changing state, the state is not immediately available, just like in React! For example:
234
+ ```ruby
235
+ class MyComponent < React::PureComponent::Base
236
+ render do
237
+ previous_state_value = state.variable
238
+ state.variable = next_state_value # even though this looks like a assignment, it causes a side effect
239
+ # state may be updated after the next render cycle
240
+ next_state_value == state.variable # very probably false here until next render
241
+ previous_state_value == state.variable # probably true here until next render
242
+
243
+ # to work with next_state_value, wait for the next render cycle, or just keep using the next_state_value variable here instead of state.value
244
+ end
245
+ end
246
+ ```
247
+ To make the side effect of a set_state more visible, state can be set by using a method call instead of a assignment:
248
+ ```ruby
249
+ class MyComponent < React::PureComponent::Base
250
+ render do
251
+ previous_state_value = state.variable
252
+ state.variable(next_state_value) # setting state with a method call, it causes a side effect
253
+ # state may be updated after the next render cycle
254
+ next_state_value == state.variable # very probably false here until next render
255
+ previous_state_value == state.variable # probably true here until next render
256
+
257
+ # to work with next_state_value, wait for the next render cycle, or just keep using the next_state_value variable here instead of state.value
258
+ end
259
+ end
260
+ ```
261
+ ### Lifecycle Callbacks
262
+ All lifecycle callbacks that are available in the matching React version are available as DSL. Callback names are underscored.
263
+ Callback names prefixed with UNSAFE_ in React are prefixed with unsafe_ in ruby.
264
+ Example:
265
+ ```ruby
266
+ class MyComponent < React::Component::Base
267
+ render do
268
+ SPAN { 'some more text' }
269
+ end
270
+
271
+ component_did_mount do
272
+ `console.log("MyComponent mounted!")`
273
+ end
274
+ end
275
+ ```
276
+
277
+ ### Events
278
+ Event names are underscored in ruby: `onClick` becomes `on_click`. The conversion for React is done automatically.
279
+
280
+
281
+ Event handlers must be declared using the `event_handler` DSL. This is to make sure, that they are not recreated during render and can be properly
282
+ compared by reference by shouldComponentUpdate(). Use the DSL like so:
283
+ ```ruby
284
+ class MyComponent < React::Component::Base
285
+ event_handler :handle_click do |event|
286
+ state.toggler = !state.toggler
287
+ end
288
+
289
+ render do
290
+ SPAN(on_click: :handle_click) { 'some more text' }
291
+ SPAN(on_click: :handle_click) { 'a lot more text' } # event handlers can be reused
292
+ end
293
+ end
294
+ ```
295
+
296
+ To the event handler the event is passed as argument. The event is a ruby object `React::SyntheticEvent` and supports all the methods, properties
297
+ and events as the React.Synthetic event. Methods are underscored. Example:
298
+ ```ruby
299
+ class MyComponent < React::Component::Base
300
+ event_handler :handle_click do |event|
301
+ event.prevent_default
302
+ event.current_target
303
+ end
304
+
305
+ render do
306
+ SPAN(on_click: :handle_click) { 'some more text' }
307
+ end
308
+ end
309
+ ```
310
+ Targets of the event, like current_target, are wrapped Elements as supplied by opal-browser.
311
+
312
+ #### Events and Function Components
313
+ The event_handler DSL can be used within the React::FunctionComponent::Creator. However, function component dont react by themselves to events,
314
+ the event handler must be applied to a element.
315
+ ```ruby
316
+ class React::FunctionComponent::Creator
317
+ event_handler :show_red_alert do |event|
318
+ `alert("RED ALERT!")`
319
+ end
320
+
321
+ event_handler :show_orange_alert do |event|
322
+ `alert("ORANGE ALERT!")`
323
+ end
324
+
325
+ function_component 'AFunComponent' do
326
+ SPAN(on_click: props.on_click) { 'Click for orange alert! ' } # event handler passed in props, applied to a element
327
+ SPAN(on_click: :show_red_alert) { 'Click for red alert! ' } # event handler directly applied to a element
328
+ end
329
+
330
+ function_component 'AnotherFunComponent' do
331
+ AFunComponent(on_click: :show_orange_alert, text: 'Fun') # event handler passed as prop, but must be applied to element, see above
332
+ end
333
+ end
334
+ ```
335
+ ### Render blocks
336
+ render or element or component blocks work like ruby blocks, the result of the last expression in a block is returned and then rendered,
337
+ but only if it is a string or a React Element.
338
+ HTML Elements and Components at any place in the blocks are rendered too.
339
+ Examples:
340
+ ```ruby
341
+ class MyComponent < React::Component::Base
342
+ render do
343
+ SPAN { "string" } # this string is rendered in a SPAN HTML Element
344
+ SPAN { "another string" } # this string is rendered in a SPAN too
345
+ end
346
+ end
347
+ ```
348
+ ```ruby
349
+ class MyComponent < React::Component::Base
350
+ render do
351
+ "string" # this string is NOT rendered, its not returned from the block and its not wrapped in a Element,
352
+ # to render it, wrap it in a element or fragment
353
+ "another string" # this string is returned from the block, so its rendered
354
+ end
355
+ end
356
+ ```
357
+ ```ruby
358
+ class MyComponent < React::Component::Base
359
+ render do
360
+ Fragment { "string" } # this string is rendered without surrounding element
361
+ 100 # this is not a string, so its NOT rendered, to render it, simply convert it to a string: "#{100}" or 100.to_s
362
+ end
363
+ end
364
+ ```
365
+ ### Rendering HTML or SVG Elements
366
+ Elements are rendered using a DSL which provides all Elements supported by React following these specs:
367
+ - https://www.w3.org/TR/html52/fullindex.html#index-elements
368
+ - https://www.w3.org/TR/SVG11/eltindex.html
369
+
370
+ The DSL can be used like so:
371
+ ```ruby
372
+ class MyComponent < React::Component::Base
373
+ render do
374
+ SPAN { 'some more text' } # upper case
375
+ span { 'so much text' } # lower case
376
+ end
377
+ end
378
+ ```
379
+ Use whichever you prefer. There are some clashes with opal ruby kernel methods, like `p 'text'`, that may have to be considered.
380
+
381
+ ### Accessibility
382
+ Props like `aria-label` must be written underscored `aria_label`. They are automatically converted for React. Example:
383
+ ```ruby
384
+ class MyComponent < React::Component::Base
385
+ render do
386
+ SPAN(aria_label: 'label text') { 'some more text' }
387
+ end
388
+ end
389
+ ```
390
+
391
+ ### Fragments
392
+ Fragments can be created like so:
393
+ ```ruby
394
+ class MyComponent < React::Component::Base
395
+ render do
396
+ Fragment do
397
+ SPAN { 'useful text' }
398
+ SPAN { 'extremely useful text' }
399
+ end
400
+ end
401
+ end
402
+ ```
403
+
404
+ ### Portals
405
+ Portals can be created like so:
406
+ ```ruby
407
+ class MyComponent < React::Component::Base
408
+ render do
409
+ Portal(`document.querySelector('div')`) do
410
+ SPAN { 'useful text' }
411
+ end
412
+ end
413
+ end
414
+ ```
415
+ Portals currently require a native DOM node as argument. (This may change to something conveniently provided by opal-browser.)
416
+
417
+ ### StrictMode
418
+ React.StrictMode can be used like so:
419
+ ```ruby
420
+ class MyComponent < React::Component::Base
421
+ render do
422
+ StrictMode do
423
+ SPAN { 'useful text' }
424
+ end
425
+ end
426
+ end
427
+ ```
428
+
429
+ ### Ref
430
+ Refs must be declared using the `ref` DSL. This is to make sure, that they are not recreated during render and can be properly
431
+ compared by reference by shouldComponentUpdate(). Use the DSL like so:
432
+ ```ruby
433
+ class MyComponent < React::Component::Base
434
+ ref :my_ref # a simple ref
435
+ ref :my_other_ref do |ref| # a ref with block
436
+ ref.current
437
+ end
438
+
439
+ render do
440
+ SPAN(ref: :my_ref) { 'useful text' } # refs can then be passed as prop
441
+ end
442
+ end
443
+ ```
444
+ If the ref declaration supplies a block, the block receives a `React::Ref` ruby instance as argument. `ref.current`may then be the ruby component or
445
+ native DOM node. ()The latter may change to something conveniently provided by opal-browser.)
446
+
447
+ ### React Javascript Components
448
+ Native React Javascript Components must be available in the global namespace. When importing them with webpack,
449
+ this can be ensured by assigning them to the global namespace:
450
+ ```javascript
451
+ import * as Sem from 'semantic-ui-react'
452
+ global.Sem = Sem;
453
+ ```
454
+ They can then be used like so:
455
+ ```ruby
456
+ class MyComponent < React::Component::Base
457
+ render do
458
+ Sem.Button(as: 'a') { 'useful text' }
459
+ end
460
+ end
461
+ ```
462
+
463
+ Some Javascript components accept another Javascript component as property, like for example React Router. The Ruby class won't work here,
464
+ instead the Javascript React component of the Ruby class must be passed.
465
+ It can be accessed by using Opals JS syntax to get the React Component of the Ruby class:
466
+ ```ruby
467
+ Route(path: '/', strict: true, component: MyComponent.JS[:react_component])
468
+ ```
469
+ Native Javascript components can be passed using the Javascript inlining of Opal, this also works for function components:
470
+ ```ruby
471
+ Route(path: '/a_button', strict: true, component: `Sem.Button`)
472
+ ```
473
+
474
+ ### Context
475
+ A context can be created using `React.create_context(constant_name, default_value)`. Constant_name must be a string like `"MyContext"`.
476
+ The context withs its Provider and Consumer can then be used like a component:
477
+ ```ruby
478
+ React.create_context("MyContext", 'div')
479
+
480
+ class MyComponent < React::Component::Base
481
+ render do
482
+ MyContext.Provider(value="span") do
483
+ MyOtherComponent()
484
+ end
485
+ end
486
+ end
487
+ ```
488
+ or the consumer:
489
+ ```ruby
490
+ class MyOtherComponent < React::Component::Base
491
+ render do
492
+ MyContext.Consumer do |value|
493
+ Sem.Button(as: value) { 'useful text' }
494
+ end
495
+ end
496
+ end
497
+ ```
498
+
499
+ ### Using React Router
500
+ First the Components of React Router must be imported and made available in the global context:
501
+ ```javascript
502
+ import * as ReactRouter from 'react-router';
503
+ import * as ReactRouterDOM from 'react-router-dom';
504
+ import { BrowserRouter, Link, NavLink, Route, Switch } from 'react-router-dom';
505
+
506
+ global.ReactRouter = ReactRouter;
507
+ global.ReactRouterDOM = ReactRouterDOM;
508
+ global.BrowserRouter = BrowserRouter;
509
+ global.Link = Link;
510
+ global.NavLink = NavLink;
511
+ global.Route = Route;
512
+ global.Switch = Switch;
513
+ ```
514
+ Only import whats needed, or import HashRouter instead of BrowserRouter.
515
+ Then the Router components can be used as an other component:
516
+ ```ruby
517
+ class RouterComponent < React::Component::Base
518
+ render do
519
+ DIV do
520
+ BrowserRouter do
521
+ Switch do
522
+ Route(path: '/my_path/:id', exact: true, component: MyOtherComponent.JS[:react_component])
523
+ Route(path: '/', strict: true, component: MyCompnent.JS[:react_component])
524
+ end
525
+ end
526
+ end
527
+ end
528
+ end
529
+ ```
530
+ The Javascript React components of the ruby class must be passed as shown above. The child components then get the Router props
531
+ (match, history, location) passed in their props. They can be accessed like this:
532
+ ```ruby
533
+ class MyOtherComponent < React::Component::Base
534
+
535
+ render do
536
+ Sem.Container(text_align: 'left', text: true) do
537
+ DIV do
538
+ SPAN { 'match :id is: ' }
539
+ SPAN { props.match.id }
540
+ end
541
+ DIV do
542
+ SPAN { 'location pathname is: ' }
543
+ SPAN { props.location.pathname }
544
+ end
545
+ DIV do
546
+ SPAN { 'number of history entries: ' }
547
+ SPAN { props.history.length }
548
+ end
549
+ end
550
+ end
551
+ end
552
+ ```
553
+ Otherwise the React Router documentation applies: https://reacttraining.com/react-router/
554
+
555
+ ### React::ReduxComponent
556
+ This component is like a React::Component and in addition to it, allows do manage its state conveniently over redux using a simple DSL:
557
+ - `store` - works similar like the components state, but manages the components state with redux
558
+ - `class_store` - allows to have a class state, when changing this state, all instances of the component class change the state and render
559
+ - `app_store` - allows to access application state, when changing this state, all instances that have requested the same variables, will render.
560
+ ```ruby
561
+ class MyComponent < React::PureComponent::Base
562
+ store.a_var = 100 # set a initial value for the instance
563
+ class_store.another_var = 200 # set a initial value for the class
564
+ render do
565
+ # in a React::ReduxComponent state can be used for local state managed by react:
566
+ state.some_var
567
+ # in addition to that, store can be used for local state managed by redux:
568
+ store.a_var
569
+ # and for managing class state:
570
+ class_store.another_var
571
+ # and for managing application wide state:
572
+ app_store.yet_another_var
573
+ end
574
+ end
575
+ ```
576
+ Provided some middleware is used for redux, state changes using `store` or `class_store` can be watched, debugged and otherwise handled by redux
577
+ middleware.
578
+
579
+ The lifecycle callbacks starting with `unsafe_` are not supported.
580
+ Overwriting should_component_update is also not supported.
581
+
582
+ **Data flow of a React::ReduxComponent:**
583
+ ![React::ReduxComponent Data Flow](https://raw.githubusercontent.com/isomorfeus/isomorfeus-react/master/images/data_flow_redux_component.png)
584
+
585
+ ### LucidApp and LucidComponent
586
+ A LucidComponent works very similar like a React::ReduxComponent, the same `store` and `class_store` is available. The difference is, that the
587
+ data changes are passed using props instead of setting component state. Therefore, a LucidComponent needs a LucidApp as outer component.
588
+ LucidApp sets up a React::Context Provider, LucidComponent works as a React::Context Consumer.
589
+ ```ruby
590
+ class MyApp < LucidApp::Base # is a React::Context provider
591
+ render do
592
+ MyComponent()
593
+ end
594
+ end
595
+
596
+ class MyComponent < LucidComponent::Base # is a React::Context Consumer
597
+ store.a_var = 100 # set a initial value for the instance
598
+ class_store.another_var = 200 # set a initial value for the class
599
+ render do
600
+ # in a LucidComponent state can be used for local state managed by react:
601
+ state.some_var
602
+ # in addition to that, store can be used for local state managed by redux:
603
+ store.a_var
604
+ # and for managing class state:
605
+ class_store.another_var
606
+ # and for managing application wide state:
607
+ app_store.yet_another_var
608
+ end
609
+ end
610
+ ```
611
+
612
+ The lifecycle callbacks starting with `unsafe_` are not supported.
613
+ Overwriting should_component_update is also not supported.
614
+
615
+ **Data flow of a LucidComponent within a LucidApp:**
616
+ ![LucidComponent within a LucidApp Data Flow](https://raw.githubusercontent.com/isomorfeus/isomorfeus-react/master/images/data_flow_lucid_component.png)
617
+
618
+ ### Development Tools
619
+ The React Developer Tools allow for analyzing, debugging and profiling components. A very helpful toolset and working very nice with isomorfeus-react:
620
+ https://github.com/facebook/react-devtools