module Isomorfeus module ReactViewHelper def mount_component(component_name, props = {}, asset = 'application_ssr.js') @ssr_response_status = nil @ssr_styles = nil thread_id_asset = "#{Thread.current.object_id}#{asset}" render_result = "
10 sleep 0.01 first_pass_finished = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.FirstPassFinished') end end # wait for transport requests to finish if first_pass_finished == 'transport' transport_busy = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.Opal.Isomorfeus.Transport["$busy?"]()') if transport_busy start_time = Time.now while transport_busy break if (Time.now - start_time) > 10 sleep 0.01 transport_busy = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.Opal.Isomorfeus.Transport["$busy?"]()') end end end end # build javascript for second render pass # guard against leaks from first pass, maybe because of a exception javascript = <<~JAVASCRIPT global.Opal.React.render_buffer = []; global.Opal.React.active_components = []; global.Opal.React.active_redux_components = []; let rendered_tree; let ssr_styles; let component; if (typeof global.Opal.global.MuiStyles !== 'undefined' && typeof global.Opal.global.MuiStyles.ServerStyleSheets !== 'undefined') { component = '#{component_name}'.split(".").reduce(function(o, x) { return (o !== null && typeof o[x] !== "undefined" && o[x] !== null) ? o[x] : null; }, global.Opal.global) if (!component) { component = global.Opal.Isomorfeus.$cached_component_class('#{component_name}'); } try { let sheets = new global.Opal.global.MuiStyles.ServerStyleSheets(); let app = global.Opal.React.$create_element(component, global.Opal.Hash.$new(#{Oj.dump(props, mode: :strict)})); rendered_tree = global.Opal.global.ReactDOMServer.renderToString(sheets.collect(app)); ssr_styles = sheets.toString(); } catch (e) { rendered_tree = e.message + "\\n" + e.stack; } } else if (typeof global.Opal.global.ReactJSS !== 'undefined' && typeof global.Opal.global.ReactJSS.SheetsRegistry !== 'undefined') { component = '#{component_name}'.split(".").reduce(function(o, x) { return (o !== null && typeof o[x] !== "undefined" && o[x] !== null) ? o[x] : null; }, global.Opal.global) if (!component) { component = global.Opal.Isomorfeus.$cached_component_class('#{component_name}'); } try { let sheets = new global.Opal.global.ReactJSS.SheetsRegistry(); let generate_id = global.Opal.global.ReactJSS.createGenerateId(); let app = global.Opal.React.$create_element(component, global.Opal.Hash.$new(#{Oj.dump(props, mode: :strict)})); let element = global.Opal.global.React.createElement(global.Opal.global.ReactJSS.JssProvider, { registry: sheets, generateId: generate_id }, app); rendered_tree = global.Opal.global.ReactDOMServer.renderToString(element); ssr_styles = sheets.toString(); } catch (e) { rendered_tree = e.message + "\\n" + e.stack; } } else { try { rendered_tree = global.Opal.Isomorfeus.TopLevel.$render_component_to_string('#{component_name}', #{Oj.dump(props, mode: :strict)}); } catch (e) { rendered_tree = e.message + "\\n" + e.stack; } } let application_state = global.Opal.Isomorfeus.store.native.getState(); if (typeof global.Opal.Isomorfeus.Transport !== 'undefined') { global.Opal.Isomorfeus.Transport.$disconnect(); } return [rendered_tree, application_state, ssr_styles, global.Opal.Isomorfeus['$ssr_response_status']()]; JAVASCRIPT # execute second render pass rendered_tree, application_state, @ssr_styles, @ssr_response_status = Isomorfeus.ssr_contexts[thread_id_asset].exec(javascript) # build result render_result << " data-iso-hydrated='true'" if rendered_tree render_result << " data-iso-nloc='#{props[:locale]}' data-iso-state='#{Oj.dump(application_state, mode: :strict)}'>" render_result << (rendered_tree ? rendered_tree : "SSR didn't work") else render_result << " data-iso-nloc='#{props[:locale]}'>" end render_result << '
' render_result end def ssr_response_status @ssr_response_status || 200 end def ssr_styles @ssr_styles || '' end end end