I'm making React UI library which uses React v18.2 and MobX v6.12. The library has React and MobX packages internally. (packed together) I want to use the library in my another project which uses React v16.6 and MobX v4.5.
// my project (React v16)
function App() {
return (
<MyLibraryReact18 />
);
}
Then these two error occurs.
First one:
index.js:439 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.
Second one:
Uncaught TypeError: Cannot read properties of null (reading 'useRef') at Object.useRef (index.js:1377:1) at useObserver (index.js:36726:1) at App (index.js:36807:1) at updateFunctionComponent (react-dom.development.js:13644:1) at updateSimpleMemoComponent (react-dom.development.js:13602:1)
I know the first one is just a warning. But the problem is the second one.
It seems to be due to a React version conflict, because if I downgrade the library to React 16 (no hooks), it works fine. (But I can't apply the downgrade, I have to use React 18).
How can I fix this? Is it possible to use React 18 with 16?
It's because it renders React v18 Component using React v16. Which means:
// my project (React v16)
import MyLibraryReact18 from "library";
function App() {
return (
<MyLibraryReact18 />
);
}
above code will be transpiled to:
// my project (React v16)
import MyLibraryReact18 from "library";
function App() {
return React.createElement(...); // equivalent to <MyLibraryReact18 />
}
In this situation React.createElement
is a React v16's method. Because the transpilation is executed in my project
(React v16).
So the React v18 library component cannot work and the error occurs:
Uncaught TypeError: Cannot read properties of null (reading 'useRef')
I've solved this problem by making an api(e.g. renderLibrary
) to render:
// my project (React v16)
import {renderLibrary} from "library";
function timeToRender() { // call this when you need
renderLibrary("root"); // will render MyLibraryReact18 on #root
}
function App() {
return <div id="root" />
}
That api is super simple:
// library
export const renderLibrary = id => {
ReactDOM.createRoot(document.elementById(id)).render(<MyLibraryReact18 />);
};