Module: Isomorfeus::ReactViewHelper

Defined in:
lib/isomorfeus/react_view_helper.rb

Instance Method Summary collapse

Instance Method Details

#mount_component(component_name, props = {}, asset = 'application_ssr.js') ⇒ Object



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/isomorfeus/react_view_helper.rb', line 3

def mount_component(component_name, props = {}, asset = 'application_ssr.js')
  thread_id_asset = "#{Thread.current.object_id}#{asset}"
  render_result = "<div data-iso-env=\"#{Isomorfeus.env}\" data-iso-root=\"#{component_name}\" data-iso-props='#{Oj.dump(props, mode: :strict)}'"
  if Isomorfeus.server_side_rendering

    # initialize speednode context
    unless Isomorfeus.ssr_contexts.key?(thread_id_asset)
      asset_file_name = OpalWebpackLoader::Manifest.lookup_path_for(asset)
      asset_path = File.join('public', asset_file_name)
      Isomorfeus.ssr_contexts[thread_id_asset] = ExecJS.permissive_compile(File.read(asset_path))
    end

    # build javascript for rendering first pass
    javascript = "global.FirstPassFinished = false;\n"
    javascript << "global.Opal.Isomorfeus['$force_init!']();\n"
    javascript << "global.Opal.Isomorfeus.TopLevel['$ssr_route_path=']('#{props[:location]}');\n"

    # if location_host and scheme are given and if Transport is loaded, connect and then render, otherwise do not render
    ws_scheme = props[:location_scheme] == 'https:' ? 'wss:' : 'ws:'
    api_ws_path = Isomorfeus.respond_to?(:api_websocket_path) ? Isomorfeus.api_websocket_path : ''
    javascript << "      var location_host = '\#{props[:location_host]}';\n      var ws_scheme = '\#{ws_scheme}';\n      var api_ws_path = '\#{api_ws_path}';\n      if (typeof global.Opal.Isomorfeus.Transport !== 'undefined' && location_host !== '' && api_ws_path !== '') {\n        global.Opal.Isomorfeus.TopLevel[\"$transport_ws_url=\"](ws_scheme + location_host + api_ws_path);\n        global.Opal.send(global.Opal.Isomorfeus.Transport.$promise_connect(), 'then', [], ($$1 = function(){\n          try {\n            global.Opal.Isomorfeus.TopLevel.$render_component_to_string('\#{component_name}', \#{Oj.dump(props, mode: :strict)});\n            global.FirstPassFinished = 'transport';\n          } catch (e) { global.FirstPassFinished = 'transport'; }\n        }, $$1.$$s = this, $$1.$$arity = 0, $$1))\n      } else { global.FirstPassFinished = true };\n    JAVASCRIPT\n\n    # execute first render pass\n    Isomorfeus.ssr_contexts[thread_id_asset].exec(javascript)\n\n    # wait for first pass to finish\n    first_pass_finished = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.FirstPassFinished')\n    unless first_pass_finished\n      start_time = Time.now\n      while !first_pass_finished\n        break if (Time.now - start_time) > 10\n        sleep 0.01\n        first_pass_finished = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.FirstPassFinished')\n      end\n    end\n\n    # wait for transport requests to finish\n    if first_pass_finished == 'transport'\n      transport_busy = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.Opal.Isomorfeus.Transport[\"$busy?\"]()')\n      if transport_busy\n        start_time = Time.now\n        while transport_busy\n          break if (Time.now - start_time) > 10\n          sleep 0.01\n          transport_busy = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.Opal.Isomorfeus.Transport[\"$busy?\"]()')\n        end\n      end\n    end\n\n    # build javascript for second render pass\n    javascript = <<~JAVASCRIPT\n      var rendered_tree = global.Opal.Isomorfeus.TopLevel.$render_component_to_string('\#{component_name}', \#{Oj.dump(props, mode: :strict)})\n      var application_state = global.Opal.Isomorfeus.store.native.getState();\n      if (typeof global.Opal.Isomorfeus.Transport !== 'undefined') { global.Opal.Isomorfeus.Transport.$disconnect(); }\n      return [rendered_tree, application_state];\n    JAVASCRIPT\n\n    # execute second render pass\n    rendered_tree, application_state = Isomorfeus.ssr_contexts[thread_id_asset].exec(javascript)\n\n    # build result\n    render_result << \" data-iso-state='\#{Oj.dump(application_state, mode: :strict)}'>\"\n    render_result << rendered_tree\n  else\n    render_result << '>'\n  end\n  render_result << '</div>'\n  render_result\nend\n"