Search code examples
reactjsfluxredux

React/Redux server side rendering initial state


I have a React/Redux application that keeps track of a user's authenticated state in local storage. The application is also set up to use server side rendering. I'm running into issues when rendering the initial application state. My server creates a new store and emits the SET_INITIAL_STATE action. This initial action on the client side reads localStorage and passes the authenticated information on to my reducers. The server, however, has no knowledge of this logged in state since I'm using stateless JWT located in local storage for authentication.

Since the server and client are out of sync at this point, I'm getting this error:

React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:

Which makes sense, because the server is trying to render an unauthenticated state.

What is the accepted standard or practice for setting this initial state that relies solely on something the client has access to?


Solution

  • I found that the OP is right, that cookie storage is the best option. If you're using react, redux, and express for a universal app the option that worked for me was https://github.com/eXon/react-cookie.

    Essentially:

    In you server side code you can use:

      import cookie from 'react-cookie';
    
      function handleRender(req, res) {
          cookie.setRawCookie(req.headers.cookie);
    
          // Create new Redux store instance
          const store = createStore(rootReducer, {
              username: cookie.load('username') || '',
              accessLevel: cookie.load('accessLevel') || userRoles.public,
          });
    
          //other react/redux/express related code
      }
    

    In your react applications, inside the components, you can just save the cookies directly:

      import cookie from 'react-cookie';
    
      //first parameter is anything you want, second is the data you want to save
      cookie.save( 'username', username );
      cookie.save('accessLevel', accessLevel);
    

    Further if you want to store an object, as I did, you can pass in JSON.stringify(your object). cookie.load will automatically parse it for you.

    In my code I called cookie.save from the actions, but you can call it directly in components or any abstraction you have.