isomorfeus-react 16.10.0 → 16.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +64 -0
  3. data/lib/browser/delegate_native.rb +70 -0
  4. data/lib/browser/element.rb +176 -0
  5. data/lib/browser/element/canvas.rb +17 -0
  6. data/lib/browser/element/media.rb +78 -0
  7. data/lib/browser/event.rb +92 -0
  8. data/lib/browser/event_target.rb +39 -0
  9. data/lib/browser/file_list.rb +125 -0
  10. data/lib/browser/iterable.rb +15 -0
  11. data/lib/isomorfeus-react-material-ui.rb +10 -0
  12. data/lib/isomorfeus-react.rb +145 -0
  13. data/lib/isomorfeus/config.rb +130 -0
  14. data/lib/isomorfeus/props/validate_hash_proxy.rb +178 -0
  15. data/lib/isomorfeus/props/validator.rb +131 -0
  16. data/lib/isomorfeus/react_view_helper.rb +130 -0
  17. data/lib/isomorfeus/top_level.rb +86 -0
  18. data/lib/isomorfeus/top_level_ssr.rb +28 -0
  19. data/lib/lucid_app/api.rb +30 -0
  20. data/lib/lucid_app/base.rb +7 -0
  21. data/lib/lucid_app/context.rb +7 -0
  22. data/lib/lucid_app/mixin.rb +20 -0
  23. data/lib/lucid_app/native_component_constructor.rb +105 -0
  24. data/lib/lucid_component/app_store_defaults.rb +36 -0
  25. data/lib/lucid_component/app_store_proxy.rb +38 -0
  26. data/lib/lucid_component/base.rb +7 -0
  27. data/lib/lucid_component/class_store_proxy.rb +41 -0
  28. data/lib/lucid_component/component_class_store_defaults.rb +38 -0
  29. data/lib/lucid_component/component_instance_store_defaults.rb +35 -0
  30. data/lib/lucid_component/event_handler.rb +17 -0
  31. data/lib/lucid_component/initializer.rb +12 -0
  32. data/lib/lucid_component/instance_store_proxy.rb +45 -0
  33. data/lib/lucid_component/mixin.rb +18 -0
  34. data/lib/lucid_component/native_component_constructor.rb +116 -0
  35. data/lib/lucid_component/reducers.rb +48 -0
  36. data/lib/lucid_component/store_api.rb +38 -0
  37. data/lib/lucid_component/styles_support.rb +37 -0
  38. data/lib/lucid_material/app/base.rb +9 -0
  39. data/lib/lucid_material/app/mixin.rb +22 -0
  40. data/lib/lucid_material/app/native_component_constructor.rb +107 -0
  41. data/lib/lucid_material/component/base.rb +9 -0
  42. data/lib/lucid_material/component/mixin.rb +20 -0
  43. data/lib/lucid_material/component/native_component_constructor.rb +118 -0
  44. data/lib/lucid_prop_declaration/mixin.rb +91 -0
  45. data/lib/react.rb +195 -0
  46. data/lib/react/active_support_support.rb +13 -0
  47. data/lib/react/children.rb +35 -0
  48. data/lib/react/component/api.rb +80 -0
  49. data/lib/react/component/base.rb +9 -0
  50. data/lib/react/component/callbacks.rb +106 -0
  51. data/lib/react/component/elements.rb +60 -0
  52. data/lib/react/component/event_handler.rb +19 -0
  53. data/lib/react/component/features.rb +47 -0
  54. data/lib/react/component/history.rb +36 -0
  55. data/lib/react/component/initializer.rb +11 -0
  56. data/lib/react/component/location.rb +15 -0
  57. data/lib/react/component/match.rb +31 -0
  58. data/lib/react/component/mixin.rb +19 -0
  59. data/lib/react/component/native_component_constructor.rb +93 -0
  60. data/lib/react/component/props.rb +59 -0
  61. data/lib/react/component/resolution.rb +70 -0
  62. data/lib/react/component/should_component_update.rb +14 -0
  63. data/lib/react/component/state.rb +52 -0
  64. data/lib/react/component/styles.rb +27 -0
  65. data/lib/react/component/unsafe_api.rb +33 -0
  66. data/lib/react/context_wrapper.rb +46 -0
  67. data/lib/react/function_component/api.rb +63 -0
  68. data/lib/react/function_component/base.rb +9 -0
  69. data/lib/react/function_component/creator.rb +32 -0
  70. data/lib/react/function_component/event_handler.rb +13 -0
  71. data/lib/react/function_component/mixin.rb +14 -0
  72. data/lib/react/function_component/resolution.rb +62 -0
  73. data/lib/react/memo_component/base.rb +9 -0
  74. data/lib/react/memo_component/creator.rb +32 -0
  75. data/lib/react/memo_component/mixin.rb +14 -0
  76. data/lib/react/native_constant_wrapper.rb +26 -0
  77. data/lib/react/pure_component/base.rb +9 -0
  78. data/lib/react/pure_component/mixin.rb +18 -0
  79. data/lib/react/ref.rb +13 -0
  80. data/lib/react/synthetic_event.rb +53 -0
  81. data/lib/react/version.rb +3 -0
  82. data/lib/react_dom.rb +47 -0
  83. data/lib/react_dom_server.rb +19 -0
  84. metadata +84 -2
@@ -0,0 +1,178 @@
1
+ module Isomorfeus
2
+ module Props
3
+ class ValidateHashProxy
4
+ def initialize
5
+ @validation_hash = { required: true, validate: {} }
6
+ end
7
+
8
+ def is
9
+ self
10
+ end
11
+ alias_method :and, :is
12
+ alias_method :has, :is
13
+ alias_method :with, :is
14
+
15
+ def cast
16
+ @validation_hash[:cast] = true
17
+ self
18
+ end
19
+
20
+ def default(v)
21
+ @validation_hash[:required] = false
22
+ @validation_hash[:default] = v
23
+ self
24
+ end
25
+
26
+ def ensure(v = nil, &block)
27
+ if block_given?
28
+ @validation_hash[:ensure_block] = block
29
+ else
30
+ @validation_hash[:ensure] = v
31
+ end
32
+ self
33
+ end
34
+
35
+ def exact_class(t_class)
36
+ @validation_hash[:class] = t_class
37
+ self
38
+ end
39
+
40
+ def greater_than(v)
41
+ @validation_hash[:validate][:gt] = v
42
+ self
43
+ end
44
+ alias_method :gt, :greater_than
45
+
46
+ def is_a(i_class)
47
+ @validation_hash[:is_a] = i_class
48
+ self
49
+ end
50
+
51
+ def keys(*keys)
52
+ @validation_hash[:validate][:hash_keys] = keys
53
+ self
54
+ end
55
+
56
+ def size(l)
57
+ @validation_hash[:validate][:size] = v
58
+ self
59
+ end
60
+ alias_method :length, :size
61
+
62
+ def less_than(v)
63
+ @validation_hash[:validate][:lt] = v
64
+ self
65
+ end
66
+ alias_method :lt, :less_than
67
+
68
+ def matches(regexp)
69
+ @validation_hash[:validate][:matches] = regexp
70
+ self
71
+ end
72
+
73
+ def max(l)
74
+ @validation_hash[:validate][:max] = l
75
+ self
76
+ end
77
+
78
+ def max_size(l)
79
+ @validation_hash[:validate][:max_size] = l
80
+ self
81
+ end
82
+ alias_method :max_length, :max_size
83
+
84
+ def min(l)
85
+ @validation_hash[:validate][:min] = l
86
+ self
87
+ end
88
+
89
+ def min_size(l)
90
+ @validation_hash[:validate][:min_size] = l
91
+ self
92
+ end
93
+ alias_method :min_length, :min_size
94
+
95
+ def negative
96
+ @validation_hash[:validate][:direction] = :negative
97
+ self
98
+ end
99
+
100
+ def optional
101
+ @validation_hash[:required] = false
102
+ self
103
+ end
104
+
105
+ def positive
106
+ @validation_hash[:validate][:direction] = :positive
107
+ self
108
+ end
109
+
110
+ def required
111
+ @validation_hash[:required] = true
112
+ self
113
+ end
114
+
115
+ def test(&block)
116
+ @validation_hash[:validate][:test] = block
117
+ self
118
+ end
119
+ alias_method :condition, :test
120
+ alias_method :check, :test
121
+
122
+ # types
123
+
124
+ def Array
125
+ @validation_hash[:class] = Array
126
+ self
127
+ end
128
+
129
+ def Boolean
130
+ @validation_hash[:type] = :boolean
131
+ self
132
+ end
133
+
134
+ def Enumerable
135
+ @validation_hash[:is_a] = Enumerable
136
+ self
137
+ end
138
+
139
+ def Float
140
+ @validation_hash[:class] = Float
141
+ self
142
+ end
143
+
144
+ def Hash
145
+ @validation_hash[:class] = Hash
146
+ self
147
+ end
148
+
149
+ def Integer
150
+ @validation_hash[:class] = Integer
151
+ self
152
+ end
153
+
154
+ def String
155
+ @validation_hash[:class] = String
156
+ self
157
+ end
158
+
159
+ # sub types
160
+
161
+ def Email
162
+ @validation_hash[:type] = :string
163
+ @validation_hash[:validate][:sub_type] = :email
164
+ self
165
+ end
166
+
167
+ def Url
168
+ @validation_hash[:type] = :string
169
+ @validation_hash[:validate][:sub_type] = :url
170
+ self
171
+ end
172
+
173
+ def to_h
174
+ @validation_hash
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,131 @@
1
+ module Isomorfeus
2
+ module Props
3
+ class Validator
4
+ def initialize(source_class, prop, value, options)
5
+ @c = source_class
6
+ @p = prop
7
+ @v = value
8
+ @o = options
9
+ end
10
+
11
+ def validate!
12
+ ensured = ensure!
13
+ unless ensured
14
+ cast!
15
+ type!
16
+ end
17
+ run_checks!
18
+ true
19
+ end
20
+
21
+ private
22
+
23
+ # basic tests
24
+
25
+ def cast!
26
+ if @o.key?(:cast)
27
+ begin
28
+ @v = case @o[:class]
29
+ when Integer then @v.to_i
30
+ when String then @v.to_s
31
+ when Float then @v.to_f
32
+ when Array then @v.to_a
33
+ when Hash then @v.to_h
34
+ end
35
+ @v = !!@v if @o[:type] == :boolean
36
+ rescue
37
+ raise "#{@c}: #{@p} cast failed" unless @v.class == @o[:class]
38
+ end
39
+ end
40
+ end
41
+
42
+ def ensure!
43
+ if @o.key?(:ensure)
44
+ @v = @o[:ensure] unless @v
45
+ true
46
+ elsif @o.key?(:ensure_block)
47
+ @v = @o[:ensure_block].call(@v)
48
+ true
49
+ else
50
+ false
51
+ end
52
+ end
53
+
54
+ def type!
55
+ if @o.key?(:class)
56
+ raise "#{@c}: #{@p} class not #{@o[:class]}" unless @v.class == @o[:class]
57
+ elsif @o.key?(:is_a)
58
+ raise "#{@c}: #{@p} is not a #{@o[:is_a]}" unless @v.is_a?(@o[:is_a])
59
+ elsif @o.key?(:type)
60
+ case @o[:type]
61
+ when :boolean
62
+ raise "#{@c}: #{@p} is not a boolean" unless @v.class == TrueClass || @v.class == FalseClass
63
+ end
64
+ end
65
+ end
66
+
67
+ # all other checks
68
+
69
+ def run_checks!
70
+ if @o.key?(:validate)
71
+ @o[:validate].each do |m, l|
72
+ send('c_' + m, l)
73
+ end
74
+ end
75
+ end
76
+
77
+ # specific validations
78
+ def c_gt(v)
79
+ raise "#{@c}: #{@p} not greater than #{v}!" unless @v > v
80
+ end
81
+
82
+ def c_lt(v)
83
+ raise "#{@c}: #{@p} not less than #{v}!" unless @v < v
84
+ end
85
+
86
+ def c_keys(v)
87
+ raise "#{@c}: #{@p} keys dont fit!" unless @v.keys.sort == v.sort
88
+ end
89
+
90
+ def c_size(v)
91
+ raise "#{@c}: #{@p} length/size is not #{v}" unless @v.size == v
92
+ end
93
+
94
+ def c_matches(v)
95
+ raise "#{@c}: #{@p} does not match #{v}" unless v.match?(@v)
96
+ end
97
+
98
+ def c_max(v)
99
+ raise "#{@c}: #{@p} is larger than #{v}" unless @v <= v
100
+ end
101
+
102
+ def c_min(v)
103
+ raise "#{@c}: #{@p} is smaller than #{v}" unless @v >= v
104
+ end
105
+
106
+ def c_max_size(v)
107
+ raise "#{@c}: #{@p} is larger than #{v}" unless @v.size <= v
108
+ end
109
+
110
+ def c_min_size(v)
111
+ raise "#{@c}: #{@p} is smaller than #{v}" unless @v.size >= v
112
+ end
113
+
114
+ def c_direction(v)
115
+ raise "#{@c}: #{@p} is positive" if v == :negative && @v >= 0
116
+ raise "#{@c}: #{@p} is negative" if v == :positive && @v < 0
117
+ end
118
+
119
+ def c_test
120
+ raise "#{@c}: #{@p} test condition check failed" unless @o[:test].call(@v)
121
+ end
122
+
123
+ def c_sub_type(v)
124
+ case v
125
+ when :email
126
+ when :url
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,130 @@
1
+ module Isomorfeus
2
+ module ReactViewHelper
3
+ def mount_component(component_name, props = {}, asset = 'application_ssr.js')
4
+ @ssr_response_status = nil
5
+ @ssr_styles = nil
6
+ thread_id_asset = "#{Thread.current.object_id}#{asset}"
7
+ render_result = "<div data-iso-env=\"#{Isomorfeus.env}\" data-iso-root=\"#{component_name}\" data-iso-props='#{Oj.dump(props, mode: :strict)}'"
8
+ if Isomorfeus.server_side_rendering
9
+
10
+ # initialize speednode context
11
+ unless Isomorfeus.ssr_contexts.key?(thread_id_asset)
12
+ asset_file_name = OpalWebpackLoader::Manifest.lookup_path_for(asset)
13
+ asset_path = File.join('public', asset_file_name)
14
+ Isomorfeus.ssr_contexts[thread_id_asset] = ExecJS.permissive_compile(File.read(asset_path))
15
+ end
16
+
17
+ # build javascript for rendering first pass
18
+ javascript = <<~JAVASCRIPT
19
+ global.FirstPassFinished = false;
20
+ global.Opal.Isomorfeus['$env=']('#{Isomorfeus.env}');
21
+ if (typeof global.Opal.Isomorfeus.$negotiated_locale === 'function') {
22
+ global.Opal.Isomorfeus["$negotiated_locale="]('#{props[:locale]}');
23
+ }
24
+ global.Opal.Isomorfeus['$force_init!']();
25
+ global.Opal.Isomorfeus['$ssr_response_status='](200);
26
+ global.Opal.Isomorfeus.TopLevel['$ssr_route_path=']('#{props[:location]}');
27
+ JAVASCRIPT
28
+
29
+ # if location_host and scheme are given and if Transport is loaded, connect and then render, otherwise do not render
30
+ ws_scheme = props[:location_scheme] == 'https:' ? 'wss:' : 'ws:'
31
+ location_host = props[:location_host] ? props[:location_host] : 'localhost'
32
+ api_ws_path = Isomorfeus.respond_to?(:api_websocket_path) ? Isomorfeus.api_websocket_path : ''
33
+ transport_ws_url = ws_scheme + location_host + api_ws_path
34
+ javascript << <<~JAVASCRIPT
35
+ var api_ws_path = '#{api_ws_path}';
36
+ if (typeof global.Opal.Isomorfeus.Transport !== 'undefined' && api_ws_path !== '') {
37
+ global.Opal.Isomorfeus.TopLevel["$transport_ws_url="]("#{transport_ws_url}");
38
+ global.Opal.send(global.Opal.Isomorfeus.Transport.$promise_connect(), 'then', [], ($$1 = function(){
39
+ try {
40
+ global.Opal.Isomorfeus.TopLevel.$render_component_to_string('#{component_name}', #{Oj.dump(props, mode: :strict)});
41
+ global.FirstPassFinished = 'transport';
42
+ } catch (e) { global.FirstPassFinished = 'transport'; }
43
+ }, $$1.$$s = this, $$1.$$arity = 0, $$1))
44
+ } else { return global.FirstPassFinished = true; };
45
+ JAVASCRIPT
46
+
47
+ # execute first render pass
48
+ first_pass_skipped = Isomorfeus.ssr_contexts[thread_id_asset].exec(javascript)
49
+
50
+ # wait for first pass to finish
51
+ unless first_pass_skipped
52
+ first_pass_finished = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.FirstPassFinished')
53
+ unless first_pass_finished
54
+ start_time = Time.now
55
+ while !first_pass_finished
56
+ break if (Time.now - start_time) > 10
57
+ sleep 0.01
58
+ first_pass_finished = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.FirstPassFinished')
59
+ end
60
+ end
61
+
62
+ # wait for transport requests to finish
63
+ if first_pass_finished == 'transport'
64
+ transport_busy = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.Opal.Isomorfeus.Transport["$busy?"]()')
65
+ if transport_busy
66
+ start_time = Time.now
67
+ while transport_busy
68
+ break if (Time.now - start_time) > 10
69
+ sleep 0.01
70
+ transport_busy = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.Opal.Isomorfeus.Transport["$busy?"]()')
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ # build javascript for second render pass
77
+ javascript = <<~JAVASCRIPT
78
+ let rendered_tree;
79
+ let ssr_styles;
80
+ let component;
81
+ if (typeof global.Opal.global.MuiStyles !== 'undefined' && typeof global.Opal.global.MuiStyles.ServerStyleSheets !== 'undefined') {
82
+ component = '#{component_name}'.split(".").reduce(function(o, x) {
83
+ return (o !== null && typeof o[x] !== "undefined" && o[x] !== null) ? o[x] : null;
84
+ }, global.Opal.global)
85
+ if (!component) { component = global.Opal.Isomorfeus.$cached_component_class('#{component_name}'); }
86
+ let sheets = new global.Opal.global.MuiStyles.ServerStyleSheets();
87
+ let app = global.Opal.React.$create_element(component, global.Opal.Hash.$new(#{Oj.dump(props, mode: :strict)}));
88
+ rendered_tree = global.Opal.global.ReactDOMServer.renderToString(sheets.collect(app));
89
+ ssr_styles = sheets.toString();
90
+ } else if (typeof global.Opal.global.ReactJSS !== 'undefined' && typeof global.Opal.global.ReactJSS.SheetsRegistry !== 'undefined') {
91
+ component = '#{component_name}'.split(".").reduce(function(o, x) {
92
+ return (o !== null && typeof o[x] !== "undefined" && o[x] !== null) ? o[x] : null;
93
+ }, global.Opal.global)
94
+ if (!component) { component = global.Opal.Isomorfeus.$cached_component_class('#{component_name}'); }
95
+ let sheets = new global.Opal.global.ReactJSS.SheetsRegistry();
96
+ let generate_id = global.Opal.global.ReactJSS.createGenerateId();
97
+ let app = global.Opal.React.$create_element(component, global.Opal.Hash.$new(#{Oj.dump(props, mode: :strict)}));
98
+ let element = global.Opal.global.React.createElement(global.Opal.global.ReactJSS.JssProvider, { registry: sheets, generateId: generate_id }, app);
99
+ rendered_tree = global.Opal.global.ReactDOMServer.renderToString(element);
100
+ ssr_styles = sheets.toString();
101
+ } else {
102
+ rendered_tree = global.Opal.Isomorfeus.TopLevel.$render_component_to_string('#{component_name}', #{Oj.dump(props, mode: :strict)});
103
+ }
104
+ let application_state = global.Opal.Isomorfeus.store.native.getState();
105
+ if (typeof global.Opal.Isomorfeus.Transport !== 'undefined') { global.Opal.Isomorfeus.Transport.$disconnect(); }
106
+ return [rendered_tree, application_state, ssr_styles, global.Opal.Isomorfeus['$ssr_response_status']()];
107
+ JAVASCRIPT
108
+
109
+ # execute second render pass
110
+ rendered_tree, application_state, @ssr_styles, @ssr_response_status = Isomorfeus.ssr_contexts[thread_id_asset].exec(javascript)
111
+
112
+ # build result
113
+ render_result << " data-iso-nloc='#{props[:locale]}' data-iso-state='#{Oj.dump(application_state, mode: :strict)}'>"
114
+ render_result << rendered_tree
115
+ else
116
+ render_result << " data-iso-nloc='#{props[:locale]}'>"
117
+ end
118
+ render_result << '</div>'
119
+ render_result
120
+ end
121
+
122
+ def ssr_response_status
123
+ @ssr_response_status || 200
124
+ end
125
+
126
+ def ssr_styles
127
+ @ssr_styles || ''
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,86 @@
1
+ module Isomorfeus
2
+ class TopLevel
3
+ class << self
4
+ def mount!
5
+ Isomorfeus.init
6
+ Isomorfeus::TopLevel.on_ready do
7
+ root_element = `document.querySelector('div[data-iso-root]')`
8
+ component_name = root_element.JS.getAttribute('data-iso-root')
9
+ Isomorfeus.env = root_element.JS.getAttribute('data-iso-env')
10
+ component = nil
11
+ begin
12
+ component = component_name.constantize
13
+ rescue Exception
14
+ @timeout_start = Time.now unless @timeout_start
15
+ if (Time.now - @timeout_start) < 10
16
+ `setTimeout(Opal.Isomorfeus.TopLevel['$mount!'], 100)`
17
+ else
18
+ `console.error("Unable to mount '" + #{component_name} + "'!")`
19
+ end
20
+ end
21
+ if component
22
+ props_json = root_element.JS.getAttribute('data-iso-props')
23
+ props = `Opal.Hash.$new(JSON.parse(props_json))`
24
+ hydrated = root_element.JS.getAttribute('data-iso-hydrated')
25
+ state_json = root_element.JS.getAttribute('data-iso-state')
26
+ if state_json
27
+ %x{
28
+ var state = JSON.parse(state_json);
29
+ var keys = Object.keys(state);
30
+ for(var i=0; i < keys.length; i++) {
31
+ global.Opal.Isomorfeus.store.native.dispatch({ type: keys[i].toUpperCase(), set_state: state[keys[i]] });
32
+ }
33
+ }
34
+ end
35
+ begin
36
+ Isomorfeus::TopLevel.mount_component(component, props, root_element, hydrated)
37
+ rescue Exception
38
+ @timeout_start = Time.now unless @timeout_start
39
+ if (Time.now - @timeout_start) < 10
40
+ `setTimeout(Opal.Isomorfeus.TopLevel['$mount!'], 100)`
41
+ else
42
+ `console.error("Unable to mount '" + #{component_name} + "'!")`
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def on_ready(&block)
50
+ # this looks a bit odd but works across _all_ browsers, and no event handler mess
51
+ %x{
52
+ function run() { block.$call() };
53
+ function ready_fun() {
54
+ /in/.test(document.readyState) ? setTimeout(ready_fun,5) : run();
55
+ }
56
+ ready_fun();
57
+ }
58
+ end
59
+
60
+ def on_ready_mount(component, props = nil, element_query = nil)
61
+ # init in case it hasn't been run yet
62
+ Isomorfeus.init
63
+ on_ready do
64
+ Isomorfeus::TopLevel.mount_component(component, props, element_query)
65
+ end
66
+ end
67
+
68
+ def mount_component(component, props, element_or_query, hydrated = false)
69
+ if `(typeof element_or_query === 'string')` || (`(typeof element_or_query.$class === 'function')` && element_or_query.class == String)
70
+ element = `document.body.querySelector(element_or_query)`
71
+ elsif `(typeof element_or_query.$is_a === 'function')` && element_or_query.is_a?(Browser::Element)
72
+ element = element_or_query.to_n
73
+ else
74
+ element = element_or_query
75
+ end
76
+
77
+ top = if hydrated
78
+ ReactDOM.hydrate(React.create_element(component, props), element)
79
+ else
80
+ ReactDOM.render(React.create_element(component, props), element)
81
+ end
82
+ Isomorfeus.top_component = top if top
83
+ end
84
+ end
85
+ end
86
+ end