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.
- checksums.yaml +7 -0
- data/Gemfile +1 -0
- data/README.md +620 -0
- data/isomorfeus-react.gemspec +23 -0
- data/lib/isomorfeus-react.rb +131 -0
- data/lib/isomorfeus/config.rb +84 -0
- data/lib/isomorfeus/top_level.rb +48 -0
- data/lib/isomorfeus/view_helpers.rb +38 -0
- data/lib/lucid_app/api.rb +22 -0
- data/lib/lucid_app/base.rb +7 -0
- data/lib/lucid_app/context.rb +7 -0
- data/lib/lucid_app/mixin.rb +17 -0
- data/lib/lucid_app/native_component_constructor.rb +70 -0
- data/lib/lucid_component/api.rb +97 -0
- data/lib/lucid_component/base.rb +7 -0
- data/lib/lucid_component/event_handler.rb +17 -0
- data/lib/lucid_component/initializer.rb +12 -0
- data/lib/lucid_component/mixin.rb +17 -0
- data/lib/lucid_component/native_component_constructor.rb +131 -0
- data/lib/react.rb +147 -0
- data/lib/react/active_support_support.rb +13 -0
- data/lib/react/component/api.rb +226 -0
- data/lib/react/component/base.rb +9 -0
- data/lib/react/component/elements.rb +78 -0
- data/lib/react/component/event_handler.rb +19 -0
- data/lib/react/component/features.rb +47 -0
- data/lib/react/component/history.rb +36 -0
- data/lib/react/component/initializer.rb +11 -0
- data/lib/react/component/location.rb +15 -0
- data/lib/react/component/match.rb +31 -0
- data/lib/react/component/mixin.rb +19 -0
- data/lib/react/component/native_component_constructor.rb +76 -0
- data/lib/react/component/native_component_validate_prop.rb +37 -0
- data/lib/react/component/props.rb +49 -0
- data/lib/react/component/resolution.rb +71 -0
- data/lib/react/component/should_component_update.rb +14 -0
- data/lib/react/component/state.rb +52 -0
- data/lib/react/component/unsafe_api.rb +33 -0
- data/lib/react/context_wrapper.rb +47 -0
- data/lib/react/function_component/creator.rb +47 -0
- data/lib/react/function_component/resolution.rb +61 -0
- data/lib/react/function_component/runner.rb +19 -0
- data/lib/react/native_constant_wrapper.rb +34 -0
- data/lib/react/pure_component/base.rb +9 -0
- data/lib/react/pure_component/mixin.rb +17 -0
- data/lib/react/redux_component/api.rb +132 -0
- data/lib/react/redux_component/app_store_defaults.rb +38 -0
- data/lib/react/redux_component/app_store_proxy.rb +46 -0
- data/lib/react/redux_component/base.rb +9 -0
- data/lib/react/redux_component/class_store_proxy.rb +50 -0
- data/lib/react/redux_component/component_class_store_defaults.rb +40 -0
- data/lib/react/redux_component/component_instance_store_defaults.rb +41 -0
- data/lib/react/redux_component/initializer.rb +14 -0
- data/lib/react/redux_component/instance_store_proxy.rb +50 -0
- data/lib/react/redux_component/mixin.rb +18 -0
- data/lib/react/redux_component/native_component_constructor.rb +119 -0
- data/lib/react/redux_component/reducers.rb +53 -0
- data/lib/react/ref.rb +19 -0
- data/lib/react/synthetic_event.rb +53 -0
- data/lib/react/version.rb +3 -0
- data/lib/react_dom.rb +31 -0
- data/lib/react_dom_server.rb +17 -0
- metadata +167 -0
checksums.yaml
ADDED
@@ -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
|
data/README.md
ADDED
@@ -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
|
+

|
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
|
+

|
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
|
+

|
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
|
+

|
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
|
+

|
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
|