Skip to content

Instantly share code, notes, and snippets.

@mauriciopasquier
Forked from ynonp/README.md
Created July 28, 2017 07:10
Show Gist options
  • Save mauriciopasquier/46109245d7f5233ca30318b821831e45 to your computer and use it in GitHub Desktop.
Save mauriciopasquier/46109245d7f5233ca30318b821831e45 to your computer and use it in GitHub Desktop.
react-rails and redux

react-rails and Redux

The main issue I found when integrating react-rails and flux/redux is managing application state. In redux I'd like to have a single application state serialized from the server and parsed in the client, and to use that state to initialize the main store. Something like:

initialState = JSON.parse(window.__APPSTATE__);

But that won't work since react-rails splits its work to 2 phases: it first reads all .js files in the renderer initialize phase and every call to the view helper only calls render.

Solution is described below by using a new renderer that:

  1. takes an @appstate attribute in its prerender options
  2. uses the before_render hook to inject JS code before calling render. That code dispatches a new action setting the application state.
  3. only then it calls the component's render function

I'm not sure it's the best solution or how to generalize it, so any feedback is welcomed.

module React
module ServerRendering
class AppstateRenderer < SprocketsRenderer
attr_accessor :appstate
def render(component_name, props, prerender_options)
# pass prerender: :static to use renderToStaticMarkup
@appstate = {}
if prerender_options.respond_to? :fetch
@appstate = prerender_options.fetch(:appstate, {})
end
super(component_name, props, prerender_options)
end
def before_render(component_name, props, prerender_options)
super(component_name, props, prerender_options)
dispatch = "window.mainStore.dispatch"
action = "window.actions.replace_state"
new_state = "'#{@appstate.to_json}'"
old_state = "window.__APPSTATE__"
"if ( #{new_state} !== #{old_state} ) #{dispatch}(#{action}(JSON.parse(#{new_state})))"
end
end
end
end
# config/initializers/react.rb
TichnutTV::Application.configure do
config.react.server_renderer = React::ServerRendering::AppstateRenderer
end
window.actions = {
replace_state: function(state) {
return { type: REPLACE_STATE, payload: state };
}
};
function mainStore(state, action) {
if ( typeof state === 'undefined' ) {
state = initialState;
}
switch( action.type ) {
case REPLACE_STATE:
return action.payload;
default:
return state;
}
}
<%= react_component('window.components.Main',
{},
{ prerender: { :appstate => @appstate }}) %>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment