I am building a little local dev/testing/documentation environment for some components which are used across different projects and so want to create them as individual npm packages.
Everything was working perfectly until I tried to use useState() in one of the components. Add this created the following errors:
Invalid hook call. Hooks can only be called inside of the body of a function component.
TypeError: Cannot read properties of null (reading 'useState')
I feel this is strange and I can't figure out what is causing it. I also tested with other hooks to see if it was just useState that was the problem but others such as useEffect and useRef also have the same problem.
Project structure is:
/components (the components to develop/test/document
/component-group
/Component
index.js
Component.js
component.scss
package.json
/library (where all the components are developed/tested/documented)
/component1
/component2
/etc (all the components that make up the library)
webpack.config.js
package.json
etc...
Components are imported into the library using a relative path because I don't won't to have to publish them to npm and update the version locally every time a change is made and needs to be tested.
As for the code for the component where useState is causing problems, this is a basic version which still causes problems:
import React, { useState } from 'react';
const TestComponent = () => {
const [testState, setTestState] = useState("testing");
return (
<p>Hello World</p>
)
}
export default TestComponent
If I comment out the useState line then Hello World is displayed, if I uncomment the line then the errors appear.
Any ideas as to what might be wrong would be really appreciated.
Thank you in advance!
Related
Since refactoring my code to move generic hooks and components to their own git submodules within my project I get TypeError: Cannot read properties of null (reading 'useMemo') whenever I call one of my custom hooks referring to useMemo.
I removed all the logic from a hook to make sure it didn't come from undefined arguments, so now my file looks like:
import { useMemo } from 'react'
export function useMyCustomHook() {
return useMemo(() => [], []) // Does nothing useful, but fails anyway
}
export function useMyCustomHookWithoutMemo() {
return [] // Does nothing useful, doesn't fail
}
I'm using next.js at the lastest version and
the project structure is like this:
components/
component.js (this is where I call useMyCustomHook imported from 'generics')
hooks/
pages/
index.js (returns component)
generics/
index.js (with export * from './hooks/useMyCustomHook')
hooks/
useMyCustomHook.js
I also have a jsconfig.json file with the following content, so I can write stuff like import Component from 'components/component':
{
"compilerOptions": {
"baseUrl": "."
}
}
Is next.js not compiling code in my generics folder? How can I get useMemo to work with this folder structure?
I tried moving useMyCustomHook.js to the hooks folder and it works there, so I'm guessing it has to do with a webpack config? I don't know much about those, that's why I love next.js
I started from scratch and moved files one by one into a libs folder, and added paths in jsconfig.json so I wouldn't have long imports into my libs and it seems to work for now. Probably a bug with next.js, webpack and git submodules
maybe you can add a console line after import line to ensure if useMemo exist. like this:
import { useMemo } from 'react';
console.log(useMemo);
the point is to find the variable whose value is null.
This might be a case of circular imports in your code. Webpack doesn't handle them well.
Make sure that you're not importing from components folder to generics or hooks. Try to run no-cycle eslint rule on your app, it might help to identify those: https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-cycle.md
useMemo() is available for react 16.8.6+, So if your react is not updated, you should update it, otherwise, i would try something like:
useMemo(()=>console.log("test),[])
and check dev tab in browser.
For me the issue was that had placed the useMemo out of the function component by mistake
I am creating a library of components. In this library, I created one component, connected it locally via npm link to my project, all work, the component was displayed. But when I decided to include styled-components to create a component. Here is my component.
import React, {FC} from 'react'
import styled from "styled-components"
import './mytbc.css'
export interface MyButtonProps{
color:string;
big?:boolean;
}
const MyCom: FC<MyButtonProps> = ({children, color, big, ...props}) => {
const MyCommon = styled.button`
background:${color};
padding:10px;
`
return (
<MyCommon>
{children}
</MyCommon>
)
}
export default MyCom
Then errors appeared in the console.
How to fix these errors and what are they related to?
I also had similar case when I was working with lerna and yarn workspaces. In my case the problem was using multiple and different versions of react, some where hoisted some were not during lerna compilation process.
According to docs
In order for Hooks to work, the react import from your application
code needs to resolve to the same module as the react import from
inside the react-dom package.
Run this command to list installed versions of react. And if you see more than one React, you’ll need to figure out why this happens and fix your dependency tree.
npm ls react
OR
yarn list react
Read more about the problem and solutions here
check that you have styled-components in your dependencies in file package.json
by:
cd project_name/src
npm install styled-components
For me the next.js tutorial https://github.com/zeit/next-learn-demo.git
has a problem at all stages past stage 2 "dynamic routing". Even though in the stages afterwards on stages 3 to 8 the dynamic routing should already be working.
What i assumed was the problems is that the file .next/routing appeared to be missing.
As such the tutorial asked implementation of code:
import { useRouter } from 'next/router';
and
const router = useRouter();
does not do anything.
leading to the error on clicking the link:
screenshot of next.js browser syntax error inspection
Why is the routing file not in the .next folder?
the react version is 16.10.2 (installed today through tutorial instructions)
the format of code is what is in the tutorial copy pasted. (no hook rule breaking)
there are no react duplicates.
I think you are using useRouter() in class component. useRouter is a hook and used only in functional components.
in class components we use withRouter HOC. "router" object is accessible as
this.props.router
I have a functional component in my own library that I'm importing into a nextJS project, and for some reason it's not recognized as a react functional component.
Example of my library code:
import React, { useState } from 'react';
export function Dialog(){
const [open, setOpen] = useState(false);
return (<>
// some jsx logic
</>)
}
importing it in my nextJS component:
import { Dialog } from '#myNamespace/library';
When I try to use
<Dialog />
I get this error:
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/warnings/invalid-hook-call-warning.html for tips about how to debug and fix this problem.
So I logged out a local react component with the Dialog and found that while the local react component seems to be on the namespace react__WEBPACK_IMPORTED_MODULE_1___default, my imported component just remains a plain function instead of a react component.
Any ideas as to why NextJS isn't importing my function as a react component?
EDIT:
Here's something really interesting: When I remove the hook, nextJS seems to think it is a react component. But when I put the hook in, it doesn't - and throws that error.
SOLUTION
For anyone who's interested - the issue was that my package was npm link into my project. So in that case there's issues with it not packaging up the components correctly.
It is possible that your react and react-dom isn't updated to the standard for hooks try yarn add react#18.6 react-dom#18.6
The external React component says Uncaught TypeError: Cannot read property 'Component' of undefined when I link as npm package.
I link a package in package.json as "react-mapbox": "https://github.com/varya/react-mapbox.git". Then I use it in code
import {render} from 'react-dom';
import MapBox from "react-mapbox";
render(
<div>
<h1>Hello, world!</h1>
<MapBox />
</div>
,
document.getElementById('example')
);
But nothing works, I get that error. The full repo is here https://github.com/varya/react-mapbox-test I made a small example to illustrate.
The react-mapbox package is my own, maybe I build it wrongly? This is its repo https://github.com/varya/react-mapbox
I built it with webpack, like that https://github.com/varya/react-mapbox/blob/master/webpack.config.js As I suppose, this build does not include react package assuming that this will be at the project which link it. But the component still do not see react object.
UPD
I added import React from 'react'; as #aulizko suggested, but this only provided React object onto a page. It still was not visible for the component.
To fix this I had to provide this changes https://github.com/varya/react-mapbox/commit/2687d66025aaa84553a79850aa8686e88f1f39d9
I required react in the code of the component as well.
And I have to build with Babel. For some reason the webpack build gives the same error even if react is required. I created a branch to illustrate this https://github.com/varya/react-mapbox/tree/webpack Now I'm happy with Babel, but with this branch you can see what's wrong with webpack if interested.
You're probably bundling your module as UMD which is causing the bundle to utilized a global React variable which doesn't exist in the consumer app. You need to export the bundle as a CommonJS or AMD module using https://webpack.github.io/docs/configuration.html#output-librarytarget. Simple add libraryTarget: 'commonjs2 or libraryTarget: 'amd' to the output key in the webpack config and make sure you are importing react in your component.
I added import React from 'react'; as #aulizko suggested, but this only provided React object onto a page. It still was not visible for the component.
To fix this I had to provide this changes https://github.com/varya/react-mapbox/commit/2687d66025aaa84553a79850aa8686e88f1f39d9
I required react in the code of the component as well.
And I have to build with Babel. For some reason the webpack build gives the same error even if react is required. I created a branch to illustrate this https://github.com/varya/react-mapbox/tree/webpack Now I'm happy with Babel, but with this branch you can see what's wrong with webpack if interested.
The thing is that the jsx-code that you see in your editor is not the code that will be executed by node or browser.
If you look into code that are generated by the babel, you'll see something like this:
(0, _reactDom.render)(React.createElement(
'div',
null,
React.createElement(
'h1',
null,
'Hello, world!'
),
React.createElement(_reactMapbox2['default'], null)
), document.getElementById('example'));
So as you can see it uses React constant under the hood.
You need to explicitely import React if you want to use jsx-code.
Add something like this in your code and it'll work:
import React from 'react'; // <!--- add this!!!
import {render} from 'react-dom';
import MapBox from "react-mapbox";
// the rest of your code goes here...
You have to import React's Component it this way:
import {Component} from 'react';
or even:
import React, { Component, PropTypes } from 'react';