简体   繁体   中英

Server Side Rendering React

I've been trying to implement server side rendering for a Redux/React application for a while now. I've set everything up according to the examples that I followed but something weird is happening.

When I look at the rendering process in the Google Chrome timeline I noticed that my html shows up for a split second, then it disappears and then it renders everything from scratch again (sort of like it ignored my server side content when React tried to mount to it and then just used normal client side rendering).

I've checked what is sent over to the client and it seems to be fine. All the html, head tags and the javascript to assign to the window is there. Also when it initially tries to render the html from the server side it looks good for that split second (I checked in Chrome timeline and looked at frame by frame images of what it is rendering).

I'm wondering if anything is immediately apparent from the following setup or any ideas what might be going on. The following is sort of pseudocode showing what I implemented. I'll edit if any of the comments need more code or info.

// client - configureStore and configureRoutes are custom 
// functions that just return the store with initial state and the routes.
const serverState = JSON.parse(window._SERVERSTATE);
const store = configureStore(browserHistory, serverState);
const history = syncHistoryWithStore(browserHistory, store);
const routes = configureRoutes(store);

render(
    <Provider store={store}>
        <Router history={history} routes={routes}/>
    </Provider>,
    document.getElementById('main')
);

// server - node.js
const initialState = setupState();
const memoryHistory = createMemoryHistory(req.url);
const store = configureStore(memoryHistory, initialState);
const history = syncHistoryWithStore(memoryHistory, store);
const routes = configureRoutes(store);

match({ history, routes, location: req.url }, (err, redirectLocation, renderProps) => {

    if (err) {
        return next(err)
    }

    if (redirectLocation) {
        return res.redirect(302, redirectLocation.pathname + redirectLocation.search)
    }

    // Fetches all data need to render components by calling their static initializeActions functions
    fetchData(store.dispatch, renderProps.components, renderProps.params)
        .then(() => {

            const body = renderToString(
                <Provider store={store}>
                    <RouterContext {...renderProps} />
                </Provider>
            );

            const helmetHeaders = Helmet.rewind();

            const state = JSON.stringify(store.getState());

            const html = `
                <!DOCTYPE html>
                <html>
                    <head>
                        ${helmetHeaders.title.toString()}
                        <link rel="stylesheet" type="text/css" href="/styles.css">
                    </head>
                    <body>
                        <div id="main">
                            ${body}
                        </div>
                        <script> 
                            window._SERVERSTATE = ${JSON.stringify(state)}
                        </script>
                        <script src="/app.js"></script>
                    </body>
                </html>
        })
})

// Typical component
class Example extends React.Component{
    static initializeActions = [ ExampleAction ]

    render() {
        <div>Hello</div>
    }
}

if your client code render again, it means what your server render is different from client render, so your client code just clear everything and render it again.

Here is What I do, just use your local bundle.js to replace your bundle.min.js to debug your appliaction, and you should find a warning about react markup checksum , and just compare what your server render and your client render, see what is the difference.

my problem is there are data-react-id property on my server. so I use ReactDom.renderToStaticMarkup to replace ReactDom.renderToString . you can try it first.

I finally figured out what was wrong. If you are using async routes with require.ensure then you have to call match before rendering client side:

match({ history, routes }, (err, redirectLocation, renderProps) => {
    render(
        <Provider store={store}>
            <Router history={history} routes={routes}/>
        </Provider>,
        document.getElementById('main')
    );
});

I've created starter for react server-side rendering. It was built using off recommendations from redux and react-router v4. Please check it out

https://github.com/gzoreslav/react-redux-saga-universal-application

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM