ericclemmons / react-resolver

Async rendering & data-fetching for universal React applications.
https://ericclemmons.github.io/react-resolver
Other
1.65k stars 52 forks source link

render called twice on server #76

Closed jachenry closed 9 years ago

jachenry commented 9 years ago

I've been noticing an issue where the componentWillMount/render methods inside my root component get called twice when rendering on the server. Attached below are some code snippets from the important parts.

Routes.js

    <Route path="/" handler={Main}>
        <DefaultRoute handler={Home} />
        <Route name="signin" path="/signin" handler={Signin}/>
        <Route name="signup" path="/signup" handler={Signup}/>
        <Route name="forgot_password" path="/users/password" handler={ForgotPassword}/>
        <Route name="reset_password" path="/users/password/edit" handler={ResetPassword}/>
        <Route name="account" path="/account/profile" handler={AccountNavigation}>
            <DefaultRoute name="account_profile" handler={AccountProfile} />
            <Route name="account_settings" path="/account/settings" handler={AccountSettings} />
        </Route>
    </Route>

server.js

const location = new ServerLocation({ request, reply });
    Router.create({ location, routes }).run(function(Handler, state) {
        Resolver.resolve(() => <Handler {...state} />).then(({Resolved, data}) => {
            reply(`
                <!DOCTYPE html>
                <html lang="en-us">
                    <head>
                                                ...
                    </head>
                    <body>
                        <div id="react-root">${React.renderToString(<Resolved />)}</div>
                        <script src="${webserver}/dist/client.js" async defer></script>
                    </body>
                </html>
            `);
        }).catch((error) => reply(error.stack).type("text/plain").code(500)); // Just in case!
    });

Main.js

...
    componentWillMount () {
        if (__SERVER__) {
            console.log("Hello server");
        }

        if (__CLIENT__) {
            console.log("Hello client");
        }
    }
...

logs

[1] ==> ✅  Server is listening
[1] ==> 🌎  Go to http://0.0.0.0:8000
[1] onPreResponse
[1] Hello server
[1] Hello server

Is anyone else seeing this issue? The issue goes away when I remove the react-resolver from the equation.

ericclemmons commented 9 years ago

That's correct.

Because React doesn't support async rendering, we have to make multiple passes through the component tree until all promises have resolved on the server.

There are a couple ways to try & solve this (#57 for example), but they're all pretty complex and will be resolved at a later date.

ericclemmons commented 9 years ago

@bbnnt Mentioned this issue from #77. Is this actually a problem? React is meant to re-render multiple times by design, and this is leveraged to generate a working server-side view.

If it's not, I'd rather close this in favor of #57.

benbonnet commented 9 years ago

I might lack of a more advanced understanding of react itself right here; I just liked the fact that for the first page load and when routing, the rendering was occurring after the data fetch. Now the page's empty for a very short amount of time — well, depending on the duration of the fetch but it still works server side as expected

ericclemmons commented 9 years ago

I think @jachenry's question was why rendering happened twice (it can actually happen many more times, depending on the depth of the tree) on the server.

As for the client, by itself, Resolver will render on the client progressively the same way a normal SPA would. There just happens to be a couple ways to:

  1. Wait for the entire tree to resolve before rendering and
  2. Render on the server & render on the client with the pre-resolved data.

Either way, until #57 happens and server-side rendering can pick up mid-tree, the multi-pass approach is the cleanest way of accomplishing this.

Again, it should not be a problem for the application as React intends for the whole tree to be re-renderable at any point in time.

benbonnet commented 9 years ago

My bad I'm sorry; I should not have pointed this one out on the other post. I was concerned about #75 (links breaks, the solution provided is what I just described right here)