Can't get hooks to work
See original GitHub issueI implemented loadable-components using ssr but I keep getting
Uncaught Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.
at invariant (http://localhost:3001/static/js/client.js:132920:15)
at resolveDispatcher (http://localhost:3001/static/js/client.js:134268:28)
at useState (http://localhost:3001/static/js/client.js:134293:20)
at SideMenu (http://localhost:3001/static/js/client.js:181039:151)
at renderWithHooks (http://localhost:3001/static/js/client.js:114473:18)
at mountIndeterminateComponent (http://localhost:3001/static/js/client.js:116555:13)
on any use of hooks.
There is an Apollo query wrapping various components including those that use hooks. I originally used react-async-component
. That did work but also seemed to cause similar hooks issues, although only with hot reloading and hard reloading on ssr pages with hooks. Anything that was behind an async loaded page was fine. I switched it out for @loadable in the hope of resolving the issues I had, but it’s way worse now. Any page with hooks
and react-apollo
now throw the above error.
Versions: react: 16.8.6 react-dom: 16.8.6 react-apollo: 2.5.5 @loadable/component: 5.9.0 @loadable/server: 5.9.0 @loadable/babel-plugin: ^5.8.0 @loadable/webpack-plugin: ^5.7.1 @material-ui: 3.9.3
I’d appreciate any help. Went through all the docs and issues but I’m completely blocked. Sorry about the long list of code.
server.js
import React from 'react';
import fs from 'fs';
import path from 'path';
import {
renderToString,
} from 'react-dom/server';
import { Provider } from 'react-redux';
import { HelmetProvider } from 'react-helmet-async';
import { StaticRouter } from 'react-router-dom';
import { SheetsRegistry } from 'jss';
import JssProvider from 'react-jss/lib/JssProvider';
import {
MuiThemeProvider,
createGenerateClassName,
} from '@material-ui/core/styles';
import { ApolloProvider, getDataFromTree } from 'react-apollo';
import fetch from 'node-fetch';
import { ChunkExtractor } from '@loadable/server';
import createClient from 'common-lib/src/components/apollo';
import { get } from 'server/modules/tenant/manager';
import preload from 'modules/preload/actions';
import { theme } from 'modules/main/components/Theme';
import configureStore from 'store/configureStore';
import App from 'App';
import clientVars from 'server/config/clientVars';
import ServerHTML from './ServerHTML';
const appDirectory = fs.realpathSync(process.cwd());
const webStatsFile = path.resolve(
appDirectory,
'build/loadable-stats-web.json',
);
const graphQLURL = process.env.RAZZLE_RUNTIME_GRAPHQL_URL;
const wsURL = process.env.RAZZLE_RUNTIME_GRAPHQL_WS;
const reactApplicationMiddleware = async (req, res) => {
try {
const client = createClient(true, { req, fetch });
const store = configureStore();
const sheetsRegistry = new SheetsRegistry();
const sheetsManager = new Map();
const generateClassName = createGenerateClassName();
const extractor = new ChunkExtractor({
statsFile: webStatsFile,
entrypoints: ['client'],
});
const reactRouterContext = {};
const helmetContext = {};
const TheApp = () => (
<ApolloProvider client={client}>
<Provider store={store}>
<JssProvider
registry={sheetsRegistry}
generateClassName={generateClassName}
>
<MuiThemeProvider
theme={theme}
sheetsManager={sheetsManager}
>
<HelmetProvider context={helmetContext}>
<StaticRouter
location={req.url}
context={reactRouterContext}
>
<App />
</StaticRouter>
</HelmetProvider>
</MuiThemeProvider>
</JssProvider>
</Provider>
</ApolloProvider>
);
if (reactRouterContext.status) {
res.status(reactRouterContext.status);
}
if (reactRouterContext.url) {
res.redirect(
reactRouterContext.status === 301 ? 301 : 302,
reactRouterContext.url,
);
return;
}
const { params } = req;
const site = await get();
await preload(
store,
clientVars,
site,
params,
);
const preloadedState = store.getState();
const jsx = extractor.collectChunks(
<TheApp configVars={clientVars} preloadedState={preloadedState} />,
);
try {
await getDataFromTree(jsx);
} catch (err) {
console.error('Error while running `getDataFromTree`', err);
}
const initialApolloState = client.extract();
const html = renderToString(
<ServerHTML
helmet={helmetContext.helmet}
css={sheetsRegistry.toString()}
state={initialApolloState}
graphQLURL={graphQLURL}
preloadedState={preloadedState}
wsURL={wsURL}
extractor={extractor}
>
{jsx}
</ServerHTML>,
);
switch (reactRouterContext.status) {
case 301:
case 302:
res.status(reactRouterContext.status);
res.location(reactRouterContext.url);
res.end();
break;
case 404:
res.status(reactRouterContext.status);
res.type('html');
res.write('<!doctype html>');
res.write(html);
res.end();
break;
default:
res.status(200);
res.type('html');
res.write('<!doctype html>');
res.write(html);
res.end();
}
} catch (err) {
console.log(err);
res.status(500).send(`Something went wrong: ${err.message}`);
}
};
export default reactApplicationMiddleware;
client.js
import React from 'react';
import { hydrate } from 'react-dom';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import { ApolloProvider } from 'react-apollo';
import { HelmetProvider } from 'react-helmet-async';
import JssProvider from 'react-jss/lib/JssProvider';
import createGenerateClassName from '@material-ui/core/styles/createGenerateClassName';
import { loadableReady } from '@loadable/component';
import Theme from 'modules/main/components/Theme';
import createClient from 'common-lib/src/components/apollo';
import configureStore from 'store/configureStore';
import clientVars from 'server/config/clientVars';
import App from './App';
const supportsHistory = 'pushState' in window.history;
const client = createClient();
const preloadedState = window.__PRELOADED_STATE__;
if (process.env.NODE_ENV !== 'production') {
delete window.__PRELOADED_STATE__;
}
export const store = configureStore(preloadedState);
const generateClassName = createGenerateClassName();
loadableReady(() => {
const app = (
<ApolloProvider client={client}>
<Provider store={store}>
<JssProvider generateClassName={generateClassName}>
<Theme>
<BrowserRouter forceRefresh={!supportsHistory}>
<HelmetProvider>
<App configVars={clientVars} />
</HelmetProvider>
</BrowserRouter>
</Theme>
</JssProvider>
</Provider>
</ApolloProvider>
);
hydrate(app, document.querySelector('#root'));
});
if (module.hot) {
module.hot.accept();
}
Issue Analytics
- State:
- Created 4 years ago
- Comments:8 (2 by maintainers)
Top GitHub Comments
I’m having the same issue. With a Razzle app, as soon as I add loadable-components to the mix, I can’t use hooks any more, same error as OP.
UPDATE:
I found it. Unlikely to be related to OPs problem, as my case is Razzle specific.
A new Razzle app has this in
server.js
:That needs to be removed! Because when one follows the loadable-components docs, one will add something like this:
And that
scriptTags
variable contains all chunks AND the mainbundle.js
- so just like the error message suggested, I was indeed loading React twice because I was loading twobundle.js
files.Took me three hours to figure this out…
Thanks man, hours and hours of research until I saw this