I have recently upgraded a React project to 18 and it's dependencies:
react-project package.json:
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1"
},
"devDependencies": {
"@types/node": "^16.11.45",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"@types/react-router-dom": "^5.3.3",
"react-app-alias-ex": "^2.1.0",
"react-app-rewired": "^2.2.1",
"typescript": "^4.7.4"
},
I have a shared react base library, which I have upgraded as well and I use it on this project:
react-shared package.json:
"dependencies": {
"dotenv": "^16.0.1",
"graphql": "^16.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.3.0",
"redux-thunk": "^2.4.1",
"typesafe-actions": "^5.1.0"
},
"devDependencies": {
"@types/node": "^18.0.6",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"@types/react-router-dom": "^5.3.3",
"tailwindcss": "^3.0.22",
"tsconfig-paths": "^4.0.0",
"typescript": "^4.7.4"
}
I am getting errors on the browser's console when I run it, saying:
Warning: 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: {...}
useOutlet hooks.tsx:214
Outlet components.tsx:110
{...}
I have created a private and a public outlet, using react-router-dom:
react-shared private.outlet.tsx:
export default class PrivateOutlet extends Component {
public constructor(props: any) {
super(props);
}
public render(): RenderResult {
if (loggedIn) {
return <Outlet />;
}
return <Navigate to="/login" replace />;
}
}
react-shared public.outlet.tsx:
export default class PublicOutlet extends Component {
public constructor(props: any) {
super(props);
}
public render(): RenderResult {
if (!loggedIn) {
return <Outlet />;
}
return <Navigate to="/" replace />;
}
}
And the application:
react-project application.tsx:
export default class ApplicationComponent extends Component {
public constructor(props: any) {
super(props);
}
public render(): RenderResult {
return (
<BrowserRouter>
<Routes>
<Route path='/' element={<PrivateOutlet />}>
<Route index element={<MainPage />} />
</Route>
<Route path='/login' element={<PublicOutlet />}>
<Route index element={<LoginPage />} />
</Route>
</Routes>
</BrowserRouter>
);
}
}
Does anyone have any idea how I can fix this problem and why is it happening? I'm trying to avoid changing my class components library to functional... Has react finally scr*wed me?
If anyone needs any more details I'll add them, just ask.
EDIT #1:
Warning: 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://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem. react.development.js:209
React (4)
printWarning
error
resolveDispatcher
useContext
useOutlet hooks.tsx:214
Outlet components.tsx:110
React (8)
renderWithHooks
mountIndeterminateComponent
beginWork
beginWork$1
performUnitOfWork
workLoopSync
renderRootSync
performConcurrentWorkOnRoot
workLoop scheduler.development.js:266
flushWork scheduler.development.js:239
performWorkUntilDeadline scheduler.development.js:533
(Async: EventHandlerNonNull)
js scheduler.development.js:571
js scheduler.development.js:633
factory react refresh:6
Webpack (24)
__webpack_require__
fn
js
factory
__webpack_require__
fn
js
js
factory
__webpack_require__
fn
js
factory
__webpack_require__
fn
js
factory
__webpack_require__
fn
tsx
factory
__webpack_require__
<anonymous>
<anonymous>
EDIT #2:
Uncaught TypeError: dispatcher is null
useContext React
useOutlet hooks.tsx:214
Outlet components.tsx:110
React (11)
renderWithHooks
mountIndeterminateComponent
beginWork
callCallback
invokeGuardedCallbackDev
invokeGuardedCallback
beginWork$1
performUnitOfWork
workLoopSync
renderRootSync
performConcurrentWorkOnRoot
workLoop scheduler.development.js:266
flushWork scheduler.development.js:239
performWorkUntilDeadline scheduler.development.js:533
js scheduler.development.js:571
js scheduler.development.js:633
factory react refresh:6
Webpack (24)
__webpack_require__
fn
js
factory
__webpack_require__
fn
js
js
factory
__webpack_require__
fn
js
factory
__webpack_require__
fn
js
factory
__webpack_require__
fn
tsx
factory
__webpack_require__
<anonymous>
<anonymous>
I've actually arrived at this solution through another question that spinned off from this one. Link here.
The problem was duplicate references due to me using a shared project and the react-app-alias-ex
package. I fixed it by adding the duplicate reference as an alias at config-overrides.js
:
const {aliasWebpack} = require('react-app-alias-ex');
const path = require('path');
const options = {
alias: {
'@my-shared': path.resolve('../PATH/TO/SHARED/src'),
'react-router-dom': path.resolve('./node_modules/react-router-dom')
}
};
module.exports = aliasWebpack(options);
There was another very weird problem that was causing this change to not work and I was able to make work by following these steps rigorously:
/node_modules
, /dist
and /package-lock.json
.npm install
.npm run react-app-rewired build
(If you are not using react-app-rewired
, running npm run react-scripts build
might work but I have not tested it).