I use server side rendering with some code splitting routes. It looks like OK on my server side with shim require.ensure
. But I get the following warning on my client side:
Warning: 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:
(client) ctid=".bqth22sav4"><noscript data-reacti
(server) ctid=".bqth22sav4"><div data-reactid=
Here is the main part of my client side codes:
const store = createStore(getInitialState());
const history = createHistory();
const { pathname, search } = window.location;
const location = createLocation(pathname + search);
match({ routes, location }, (err, redirectLocation, renderProps) => {
render(
<Provider store={store}>
<Router
{...renderProps}
history={history}
/>
</Provider>,
document.getElementById('mount')
);
});
Regards.
@anatomic Hi, bro.
I saw your solution (https://github.com/rackt/react-router/issues/1402), but what about 1.0.0-rc1
?
Thanks.
How do you render this on server? I am using redux with react-rotuer 1.0.0b2 without this trouble, check my code here https://github.com/vojtatranta/Photostore/blob/master/index.js.
@vojtatranta I didn't find any code splitting routes from your Photostore
project.
Here is my code splitting routes (using getComponent
):
import { Route } from 'react-router';
import React from 'react';
import ac from 'utils/asyncComponent';
import Layout from 'containers/Layout';
export default (
<Route component={Layout}>
<Route path="/" getComponent={ac('containers/Home')} />
<Route path="/post/:id" getComponent={ac('containers/Post')} />
<Route path="*" getComponent={ac('containers/NotFound')} />
</Route>
);
utils/asyncComponent:
const asyncImport = (moduleName) => __CLIENT__ ?
require(`promise?global!../${moduleName}/index`)() :
new Promise((resolve, reject) => {
try { resolve(require(`../${moduleName}/index`)); }
catch (err) { reject(err); }
});
export default (moduleName) => (location, cb) =>
asyncImport(moduleName)
.then(component => cb(null, component))
.catch(cb);
Anyway, thx.
I get a similar issue.
The client rendering seems to render once without any content, producing the noscript tag result, then renders again with correct contents.
I render simply by doing a Router render
ReactDOM.render(<Router routes={routes} />, document.findElementById('viewport'));
The client rendering seems to render once without any content, producing the noscript tag result, then renders again with correct contents.
Yep, seeing the exact same issue here.
Same here with 1.0.0-rc3.
How is everyone rendering on the server side? I'm doing it like this verbatim:
match({ routes, history, location }, (error, redirectLocation, renderProps) => {
if (redirectLocation) {
res.redirect(301, redirectLocation.pathname + redirectLocation.search);
} else if (error) {
res.send(500, error.message);
} else if (!renderProps) {
res.send(404, 'Not found');
} else {
const assets = webpackIsomorphicTools.assets(),
initialState = serialize(store.getState());
const root = renderToString(
<Provider store={store}>
<RoutingContext {...renderProps}/>
</Provider>
);
res.render('index', {
config,
assets,
initialState,
root
});
}
});
Almost, but except for history
.
Here's my server side snippet:
match({ routes, location }, (error, redirectLocation, renderProps) => {
// ... almost the same with yours.
});
I don't think the problem causes from server side. Because (server) ctid=".bqth22sav4"><div data-reactid=
is what I want, but (client) ctid=".bqth22sav4"><noscript data-reacti
isn't.
Oh yeah, history is because I'm still on rc1. Ignore that :smile:
@u9520107 and @Nic128, are you both doing async getComponent
calls?
I could have sworn there was another issue for this. Maybe it was a discussion on Slack. This is a bug with using async routes and server-side rendering, since the server won't render until the routes are loaded, but when the client does the initial render, those routes will still be missing.
Potentially need some way in these cases to defer initial client render until async routes are loaded or something.
@timdorr Actually, You made me realize I am executing onEnter while fetching async data. It causes an initial render of nothing until it resolves and the callback is executed.
requireAuth(nextState, replaceState, callback) {}
Then, I realized I could print that initial call server side, dump it into the page before initial render and simply not call my async method for that first render. It's a bit hacky. But it solved my problem.
This sounds like an issue with your bundler ... or am I missing something?
If your initial render's checksum doesn't match, it just means you didn't generate the same HTML as when you were on the server. In order to do that, you need to run the same code you ran on the server. Couldn't you solve this with webpack so that all of your require.ensure
's for a particular route are loaded in the bundle for that page?
/cc @petehunt who was working on a branch with a <Route asyncComponent>
prop...
@mjackson It seems fairly tricky to get all of the split up bundles sent to the client if you don't even know what they are until after you've finished matching routes, no? Is there some middleware that makes this easy?
Eventually everything would be loaded and and the right contents are rendered..
What puzzles me is that initial render with no content at all.
It's not even about matching checksums, why is there a render when the async route packages haven't ben loaded yet?
@timdorr Yes, I am doing async Component as well. If you see the following logs, '@afterEnter' and '@getComponent' is logged right before calling the callback in onEnter and getComponent respectly. The first render happened before all the callbacks are called. The top layer of component was loaded, but was not even rendered at all. The log still says that the first render ended up with 'noscript'.
@afterEnter
application.js:232 @getComponent
warning.js:25 Warning: 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:
(client) <noscript data-reacti
(server) <div data-reactid=".1warning @ warning.js:25ReactMount._mountImageIntoNode @ ReactMount.js:400ReactPerf.measure.wrapper @ ReactPerf.js:27mountComponentIntoNode @ ReactMount.js:150Mixin.perform @ Transaction.js:28batchedMountComponentIntoNode @ ReactMount.js:154Mixin.perform @ Transaction.js:28ReactDefaultBatchingStrategy.batchedUpdates @ ReactDefaultBatchingStrategy.js:33batchedUpdates @ ReactUpdates.js:64ReactMount._renderNewRootComponent @ ReactMount.js:229ReactPerf.measure.wrapper @ ReactPerf.js:27ReactMount._renderSubtreeIntoContainer @ ReactMount.js:270ReactMount.render @ ReactMount.js:277ReactPerf.measure.wrapper @ ReactPerf.js:27callee$2$0$ @ client-router.js:44tryCatch @ runtime.js:32invoke @ runtime.js:177prototype.forEach.prototype.(anonymous function) @ runtime.js:56invoke @ runtime.js:77enqueueResult @ runtime.js:95Promise @ es6.promise.js:193enqueue @ runtime.js:94prototype.forEach.prototype.(anonymous function) @ runtime.js:56runtime.async @ runtime.js:105callee$2$0 @ client-router.js:43(anonymous function) @ match.js:40(anonymous function) @ useRoutes.js:74(anonymous function) @ useRoutes.js:103done @ AsyncUtils.js:50(anonymous function) @ AsyncUtils.js:56getComponent$ @ application.js:233tryCatch @ runtime.js:32invoke @ runtime.js:177prototype.forEach.prototype.(anonymous function) @ runtime.js:56invoke @ runtime.js:77run @ es6.promise.js:91(anonymous function) @ es6.promise.js:105flush @ $.microtask.js:22
application.js:149 @afterEnter
application.js:232 @getComponent
Why wouldn't there be a render if you're synchronously doing React.render(<Router ... />, ...)
? I mean, you told React to render. It's going to render _something_.
@taion Right... Wonder if it's possible to do match and RoutingContext on client side, similar to how server side is doing things, yet still properly handle routing after the initial render.
My previous tries with doing RoutingContext renders ended up with static pages that does handle client-routing.
In principle something like the solution in the OP with match
on the client side ought to work. I don't really know why it doesn't in the OP's case.
match
is intended for server-side use, though, so I'm not 100% sure it's the "right" way to solve this, but it ought to at least work.
@taion Well... Router seems to disregard the stuff fetched during match though. In my logs, getComponent gets called twice with OP's method. On the other hand, RoutingContext would render correctly, but path handling would not work, probably because the handlers aren't being setup as the component is intended for server uses.
Imo, enabling Router to be compatible with match seems like a better idea. Keeping RoutingContext strictly for server use without needing to bind history api handling logic should keep the server code simpler.
Looking at the source code for Router, it actually initializes with nulls for components and routes.. then create a new RoutingContext using those null properties for the first render. Which explains why there's a n initial render of noscript tag. It does not try to reuse the renderProps generated by match at all.
Gonna try making Router copmonent re-use those and see if that'd solve the problem.
Naively, if we just take the properties that was generated by match:
//Router.js
class Router extends Component {
... omit code ...
constructor(props, context) {
super(props, context)
this.state = {
location: props.location || null, //try to use props if exists
routes: props.routes || null,
params: props.params || null,
components: props.components || null
}
}
... rest of the code ...
Then the initial render would be correct. However, getComponent is still called twice. So whatever that is driving the Router's async fetching logics does not take these props into account.
Huh, I thought the way it'd work would be that, after the initial async load, the stuff that was require.ensure
d would load synchronously from there on. Guess not.
I initially ran into the same problem as the OP and I got rid of it by loading the required chunks prior to the main entry file, based on the chunks required on the server during rendering. That way rendering on the client just works as expected. An initial match
will trigger all those chunks to be properly initialized so that when we render <Router>
everything is available.
@johanneslumpe Can you show us some sample code? I use match to trigger all the fetching prior to rendering as well, but still getting the same warning when rendering
Rendering on the client looks like this:
match({ routes, location }, () => {
// doing the store hydration here because I am splitting reducers
store.dispatch({
type: HYDRATE,
payload: {
...initialState
}
});
const element = document.getElementById('app-container');
ReactDOM.render((
<Provider store={store}>
<Router history={history} routes={routes} />
</Provider>
), element);
});
@u9520107 I double checked and the previous issue which lead me to preload the required chunks does not appear anymore when I do not preload them. So this seems to not be necessary anymore. The initial match
on the client forces the chunks to be loaded using require.ensure
.
So all in all my stuff does not seem to be any different from the OP's data. One thing to note is that I am not using getComponent
but getChildRoutes
to specify my split points.
@taion what do you mean with it's tricky to sent the required bundles to the client? Are you referring to linking the chunks inside the page as separate script tags? If so, there is a workable solution for that, but it doesn't seem to be required anymore (everything works fine without preloading those chunks for me, but of course having them in the html might speed the initial load up a bit)
@johanneslumpe I am referring to linking the chunks inside the page as separate script tags. What do you use to do that, BTW?
@taion I hooked into webpack's require.ensure
to track which chunks are loaded on the server and then I link them :)
For me it's a bit more complex to do as I have a different build process for server and client (some different plugins) and since I use hashes I need to write them to a json file and read that file in the server process again so I am able to map the chunk to the proper filename.
@johanneslumpe @u9520107
To close the loop on this, does everything work when using require.ensure
and webpack, when rendering the <Router>
inside the callback to match
?
@taion I am using jspm instead of webpack, which there's no require.ensure. I'll do more tests on making all the calls synchronous whenever possible, and see if that'd solve the noscript warning. But that might not happen for a while.
The reason the match
thing works is because require.ensure
does Zalgo, unless I'm mistaken. It's async if the chunk is loaded but sync otherwise.
If the JSPM equivalent is always async, I actually don't see how we could resolve this with the current API. ReactDOM.render
is a sync API so we'd have to render noscripts
s in the place of anything we failed to resolve synchronously.
I'm going to leave this issue open as an enhancement request for the time being. I don't think this is a bug per se, but we might be missing functionality needed for things to work well w/e.g. JSPM's async imports.
The thing is, if i use match to load the async parts, and pass the loaded components and routes to Router, Router would ignore it with current code.
Router always initailizes with a state with routes and components set to null, even if you try to pass those in as props, and it'd render the first RoutingContext with those null states.
In the test code I tried above, what I did was to let Router use the routes and components passed in via props, and was able to render correctly. This would prevent the warning for showing up.
Essentially, all the async components and routes are fetched during match, and so when rendering Router, it doesn't have to call the async functions again.
Sure. I have the same problem myself in https://github.com/rackt/react-router/issues/2088.
The horrible, dumb solution that would fix everything would just be to mutate the route object and update component
or components
as needed after getComponent
or getComponents
invokes the callback, so that things actually become synchronous after the first resolution.
Not sure if that's a viable solution though.
@taion do you mean have developers mutate the object themselves or have Router handle it?
@taion I have to confirm this again locally but last time I tried to remove the chunks from the initial html. Then webpack loaded them while i was match
ing the route. So that was async, which means it should just work for jspm?!
Also: I am using getChildRoutes
, not getComponent
so that this is working for me might be related to that.
@johanneslumpe I think the issue is that JSPM might always be async, even after initial load?
@doctyper The option I'm talking about is to handle this on the router. Again I'm not at all sure this is viable, just brainstorming out loud.
@taion ah, got it. Not sure about that.
@taion Yes, the System.import
call always returns a promise: https://github.com/systemjs/systemjs/blob/master/docs/system-api.md#systemimportmodulename--normalizedparentname---promisemodule
Makes sense. Avoids Zalgo.
@mjackson @ryanflorence
What do you think about using the dumb nuclear option fix for this and only running getComponent
or getComponents
once per route object, and just mutating the route object in place after the async call resolves? And likewise for getChildRoutes
?
This would break the use case where these functions don't always return the same value, but... come on that's be a super weird thing to do anyway.
@taion this does not work for getChildRoutes
. The idea there is exactly that you can return different route subsets based on the parameters.
Drat. I see. Same actually for getComponent
and getComponents
then, unless we change the API.
@taion changing the api to now allow different return values does not seem like something we should do. The current api allows for a the "Pinterest-style" inline view for a route when viewed from a timeline and the detail view when freshly loading the url. This was one of the big new things, so that should stay IMHO :)
I see - I think. You mean completely change which split-out component gets loaded asynchronously based on something in location.state
or whatever? Makes sense.
Hmm - I guess then the only other option would be to somehow pass the renderProps
through from match
or an equivalent down through the Router?
It's probably possible to do something similar to require.ensure with jspm. If I cache whatever's asynchronously fetched during match, then getComponent and getChildRoutes could work synchronously when rendering Router. But my previous attempts didn't really work.
Passing renderProps like the code I've shown before would actually render correctly. I reported that the Router would still attempt to fetch those items later, but that could be a false alarm. In my test code, I wasn't caching the values I generated during async calls properly, so Router would receive a different route object and component every time it has to call those async functions. Which might have been the cause of the bug. I'll try to test this out soon after I clear up my pending tasks...
Is that the best option though? Seems a little bit strange to patch Router
to also accept renderProps
coming in and not do its own initial match. I don't know. It'd be great if we could just solve the JSPM piece without forcing you to do extra work on your side.
@mjackson thoughts on this?
Until this is fixed I have a temporary solution for those using JSPM
/systemjs
:
I have a getRoute
wrapper that I use with every getComponent
that takes a path to load the default
export
to use as the component
.
This does different things depending on whether it is run on the client or server.
On the server all the routes' getComponent
s are transformed into component
s using require
on initialization.
function getRoutes(getRoute) {
return (
<Example getComponent={getRoute('path/to/route')} />
);
}
function mapRouteToStatic(r) {
if (r.getComponent) {
r.component = r.getComponent;
delete r.getComponent;
}
if (r.childRoutes) {
r.childRoutes = _.map(r.childRoutes, mapRouteToStatic);
}
return r;
};
const routes = mapRouteToStatic(
createRouteFromReactElement(
getRoutes((path) => require(`./${path}`))
)
);
On the client, the 'getRoute' uses System.get
/System.import
to load the resource correctly.
function getRoute(path) {
return (location = '', callback) => {
// Required to stop white flash on page load
const cached = System.get(`${window.location.origin}/${path}.js`);
if (cached) callback(null, cached);
// Normal async loading
return System.import(`/${path}`)
.then((c) => callback(null, c))
.catch((err) => callback(err));
};
}
When the client first loads, I have to match the route and then manually call getComponent
on every matching route to make sure they've all loaded.
const location = history.createLocation(window.location.pathname);
match({ routes, location }, (error, redirectLocation, renderProps) => {
Promise.all(
_.map(
_.filter(renderProps.routes, 'getComponent'), (route) =>
new Promise((resolve, reject) => route.getComponent(renderProps.location, resolve)
)
)
)
.then(() => ReactDOM.render(<Wrapper store={store} />, rootElement));
});
This "works", but it's a pretty bad hack and only works for getComponent
in its current form.
Just saw that 1.0.0-rc4
is out. Looks like some commits may address this issue?
Ahh... not really - those weren't intended for this case, no.
Oh...
oh, no. need solution for this case... our next release depends on it.
Just to be clear, this is only an issue if you use JSPM. If you use webpack, there shouldn't be an issue - the above solution should be sufficient.
This happens to me on webpack :( What would be the solution? Even after rereading the thread, I didn't seem so clear to me (we could talk about this on Reactiflux, if needed).
For the time being, use match
on the client, then render in the callback - this will ensure that the relevant require.ensure
calls have already been made and returned; they aren't async on calls after the first (assuming they succeeded), and things should work from there.
I'd like to have a better solution, but I don't think we have anything to offer you at the moment.
I'm going to take a look this week too.
On Tue, Nov 3, 2015 at 7:26 PM Jimmy Jia [email protected] wrote:
For the time being, use match on the client, then render in the callback
- this will ensure that the relevant require.ensure calls have already
been made and returned; they aren't async on calls after the first
(assuming they succeeded), and things should work from there.I'd like to have a better solution, but I don't think we have anything to
offer you at the moment.—
Reply to this email directly or view it on GitHub
https://github.com/rackt/react-router/issues/2036#issuecomment-153541487
.
tried add match on client with webpack. problem resolved. thanks.
I made a demo, I'm also perfectly happy with using match
to tickle the code loading:
https://github.com/rackt/example-react-router-server-rendering-lazy-routes
What should we do for e.g. JSPM though?
Nothing. We provide a callback, which is the most primitive thing we can do for asynchrony and code splitting.
Woops, didn't mean to submit yet. There's got to be a way to make getChildRoutes
callback synchronously with jspm, maybe you just have to keep your own "cache" of the module. match
will trigger code to load, then next time around in render, the app can return what it found with match.
In other words, if you wanted to provide the routes as "initialState" then you're able to hang onto that stuff on your own and call back with it in getRoutes
during render, after match.
@ryanflorence Hi, Have you tried
export default {
path: '/',
component: App,
getChildRoutes(location, cb) {
if (typeof window !== 'undefined') {
console.log("set sleep");
setTimeout(() => {
require.ensure([], (require) => {
cb(null, [ require('./AboutRoute') ])
})}, 10000);
}else{
console.log("server render");
require.ensure([], (require) => {
cb(null, [ require('./AboutRoute') ])
});
}
},
indexRoute: {
component: Index
}
}
and access url localhost:5000/about directly. I've got noscript tag
@ryanflorence thanks so much for that example, fixed my issue. This is awesome!
Does the webpack solution still hold true in 2.0.0?
@lostthetrail haven't tried it but according to the change log, require.ensure
is always async in 2.0.0. Not sure if that will cause the same issues people are having with JSPM. Did you give it a go?
I have tried to get @ryanflorence's example to work with webpack 2.0.7-beta on this fork https://github.com/djeeg/example-react-router-server-rendering-lazy-routes
Directly accessing http://localhost:5000/about
Gives
Warning: 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:
(client) <noscript data-reacti
(server) <div data-reactid=".n
I'm getting the same issue ... I looked at @ryanflorence's example, and didn't get the error message. Then discovered it was being suppressed by running the server in production mode from the npm start script.
I wrote my own quick example that can be found here:
https://github.com/leepowellcouk/react-router-2036
It doesn't do any code splitting, the only thing it does is perform a crude async getComponent on the 'about' route which seems to trigger this warning.
Warning: 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:
(client) <noscript data-reacti
(server) <div data-reactid=".2
@leepowellcouk I quickly looked over your repo, it looks like mine and I'm not having problems. Are you sure you didn't make changes to the routes without restarting the server?
Thanks for your question!
We want to make sure that the GitHub issue tracker remains the best place to track bug reports and feature requests that affect the development of React Router, and per the issue template, we are not handling support requests here.
If you have a question or otherwise need help, please post on Stack Overflow with the #react-router tag at https://stackoverflow.com/questions/ask?tags=react-router, or drop in on the #react-router room on Reactiflux at https://discord.gg/0ZcbPKXt5bYaNQ46.
@zackify I'm sure. Restarted the server numerous times. If you have time I'd be grateful if you could run it and see if you get the same problem?
Yep, chiming in with the same problem when I have an async onEnter function:
const routes = {
// ...
{ path: '/dancing',
component: Dancing,
onEnter: (nextState, replace, callback) => {
setTimeout(callback, 10);
} }
}
... This causes the error everyone else is getting:
build.js:1504 Warning: 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:
(client) <noscript data-reacti
(server) <div data-reactid=".k
.. But if I take out the setTimeout and call the callback syncronously it works fine.
Please either consult the guides or use one of the support forums linked above.
@taion But these are people reporting a bug. Surely this is the right place to be posting that?
I've got the same issue. Webpack ^1.12.14, async route components, server-side rendering.
This is not a bug with React Router. Again, this use case is fully covered by the docs. I'd point you to the relevant section, but we have an explicit policy against covering support requests on the issue tracker. Please use either Stack Overflow or Reactiflux if you need help with this.
Sorry but I can't see how this isn't a bug with React Router - I provided a simple use case demo, with no code splitting etc. Just a simple server side render with a async onEnter which triggers the issue. Using match on the client side didn't work and require.ensure is redundant without the code split.
The issue here is that your "simple use case demo" is incorrect. Specifically, there are errors in your code that are the reason you obtain the result you observe. Again, I invite you to reach out on Stack Overflow or Reactiflux if you need help with using React Router.
@leepowellcouk I've sent the PR https://github.com/leepowellcouk/react-router-2036/pull/1
please get in touch on Reactiflux or Stackoverflow, you can ping me a link on Twitter and I'll be happy to help. @taion spends a lot of time on this repo, and it is not like he is closing issues without a reason.
Thank you to @knowbody and @taion
Did anyone fix this issue here? I've been struggling with this issue for a month..
My problem was fixed by moving to this in the client:
match({ history, routes }, (error, redirectLocation, renderProps) => {
render(<Router {...renderProps} />, mountNode)
})
... as listed here. I made a proof-of-implementation using redux and react-router, and it works great.
Yes, can only confirm -- this worked for me, and for @leepowellcouk as well I believe. Thanks for the improved documentation, @taion.
Yes, this worked for me.
@josephg I fixed it. react-router needed to know the current route, otherwise the client and server renders with different checksum. so if the current route is passed to both the client and server, they will use the reused markup with valid checksum. that's why both needs routes
in match
!
For future Googleability, with React 15 this warning now reads:
warning.js:44 Warning: 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:
(client)
Most helpful comment
My problem was fixed by moving to this in the client:
... as listed here. I made a proof-of-implementation using redux and react-router, and it works great.