Loadable Components project is young, but please before asking your question:
Hi all, I am confused by the example for server-side-rendering.
In the example, there is both a nodeExtractor and a webExtractor. The nodeExtractor ends up using requireEntryPoints but webExtractor does not. Could someone explain to me the need for an extractor for node and a separate one for web? Furthermore, why does nodeExtractor utilize requireEntryPoints instead of specifying entrypoints in the constructor of ChunkExtractor?
Why are the above points also missing from the documentation?
Furthermore, I see a lot of specific changes to webpack.config.babel.js and babel.config.js that was not mentioned in the Documentation. What were the need for these changes in the example? Does the documentation need to be updated to reflect the same?
Would anyone also be able to explain why I run into this warning
loadable: `loadableReady()` requires state, please use `getScriptTags` or `getScriptElements` server-side
even though I can see injected script tags from SSR on HTML document in client? The behavior I'm seeing is that SSR works fine, but as soon as CSR occurs, the entire content of the app is removed and I receive errors in addition to the above warning.
These are the errors:
react-dom.development.js:55 Uncaught Invariant Violation: Element ref was specified as a string (item2126QORYRD96) but no owner was set. This could happen for one of the following reasons:
1. You may be adding a ref to a function component
2. You may be adding a ref to a component that was not created inside a component's render method
3. You have multiple copies of React loaded
Hello @imthinhvu, I don't have time to dive in your problem. But I am sure somebody will be able to help you!
@neoziro - Even if you can't dive into my problem, can you at least explain the differences between the example and the documentation? It seems you were the main contributor for the SSR example
What are the differences between example and documentation?
@neoziro - I laid out the differences when I opened the issue, can you please take a look if you haven't yet? Primarily referencing need for nodeExtractor vs webExtractor, and use of requireEntrypoints -- these are all part of the SSR example, but not in the documentation.
@imthinhvu this is because in the example, the server code is built with webpack. That's why we have two extractors. One is just for getting the entrypoint (nodeExtractor), the other is the same described in documentation (webExtractor). requireEntrypoints is used to get the entrypoint server-side, as described in the example. It ensures that the require cache is cleaned in development (process.env.NODE_ENV !== 'production').
Thanks for the response! We were able to get Loadable Components working and will submit a PR to update the documentation for others.
Hello, I am facing the same warning:
loadable:
loadableReady()requires state, please usegetScriptTagsorgetScriptElementsserver-side
I followed the SSR example, also use two extractors, webpack to build the app, and an express instance running webpack-dev-middleware + webpack-hot-middleware in development (just like the example, or at least I believe so?).
My stack is slightly different since I also use react-helmet, react-router-dom and react-apollo (GraphQL client), but that seems to work without issue.
My webpack entry for the client looks like:
entry: ['webpack-hot-middleware/client?name=web', path.resolve(__dirname, 'src/app/index.js')],
output: {
chunkFilename: '[name].chunk.js',
filename: '[name].js',
path: path.resolve(__dirname, '../build/web'),
publicPath: '/'
}
And the server one:
entry: path.resolve(__dirname, 'src/app/App.js'),
output: {
chunkFilename: '[chunkhash:8].chunk.js',
filename: '[hash:8].chunk.js',
libraryTarget: 'commonjs2',
path: path.resolve(__dirname, '../build/node'),
publicPath: '/'
}
server entry (App.js) is a classic react component rendering a <Switch /> from react-router-dom ;
And client entry (index.js) looks like this:
import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { ApolloProvider } from 'react-apollo';
import { loadableReady } from '@loadable/component';
import client from './apollo';
import App from './App';
loadableReady(() =>
render(
<ApolloProvider client={client}>
<BrowserRouter>
<App />
</BrowserRouter>
</ApolloProvider>,
document.getElementById('app')
)
);
if (process.env.NODE_ENV === 'development' && typeof module.hot !== 'undefined') {
module.hot.accept(); /* eslint-disable-line no-undef */
}
When the SSR app does:
const nodeExtractor = new ChunkExtractor({
statsFile: path.resolve(__dirname, '../build/node/loadable-stats.json')
});
const { default: App } = nodeExtractor.requireEntrypoint();
const webExtractor = new ChunkExtractor({
statsFile: path.resolve(__dirname, '../build/web/loadable-stats.json')
});
const apolloClient = new ApolloClient({
// ...
});
const AppWrapper = (
<ChunkExtractorManager extractor={webExtractor}>
<ApolloProvider client={apolloClient}>
<StaticRouter location={request.url} context={context}>
<App />
</StaticRouter>
</ApolloProvider>
</ChunkExtractorManager>
);
// Apollo data preload
await getDataFromTree(AppWrapper);
const jsx = ReactDOMServer.renderToString(AppWrapper);
const helmet = Helmet.renderStatic();
const state = apolloClient.getState();
response.send(`
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
${helmet.title.toString()}
${helmet.link.toString()}
${helmet.meta.toString()}
${webExtractor.getLinkTags()}
${webExtractor.getStyleTags()}
</head>
<body>
<div id="app">${jsx}</div>
<script>window.__APOLLO_STATE__ = ${JSON.stringify(state).replace(/</g, '\\u003c')};</script>
${webExtractor.getScriptTags()}
</body>
</html>
`)
Have you been able to isolate the cause of the warning?
And/or would you see any obvious reason here?
(I can provide a repository to reproduce the issue if you have time to investigate?)
I think the issue may be the HMR actually, I ended up doing that for now in my client webpack entry
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { ApolloProvider } from 'react-apollo';
import { loadableReady } from '@loadable/component';
import client from './apollo';
import App from './App';
const renderApplication = renderMethod =>
renderMethod(
<ApolloProvider client={client}>
<BrowserRouter>
<App />
</BrowserRouter>
</ApolloProvider>,
document.getElementById('app')
);
if (process.env.NODE_ENV === 'development' && typeof module.hot !== 'undefined') {
renderApplication(ReactDOM.render);
module.hot.accept(); /* eslint-disable-line no-undef */
} else {
loadableReady(() => renderApplication(ReactDOM.hydrate));
}
Might be stupide though ;
I don't see the warnings neither in dev or production mode, but might be missing the entire point?
@imthinhvu - I'm running into the exact same problem you described. Mind sharing your solution?
@imthinhvu - I'm having the same issue as described. Could you please share the solution.
Most helpful comment
@imthinhvu - I'm running into the exact same problem you described. Mind sharing your solution?