I am creating a monorepo to store both the web app and expo app for a project. I have setup using https://github.com/altick/cra-expo-monorepo . I have created a shared folder to store the common code.
I am facing an issue importing a react component which uses a hook into the expo app, it gives the following error
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/link/invalid-hook-call for tips about how to debug and fix this problem.
I also faced the same issue in the react web app, I found out it was due to multiple react copies( both in the app and the shared one) but I was able to solve it by updated the webpack config to use the apps react through webpack alias. However, I dont know how to achieve the same with expo.
An example component
import React,{useState} from "react";
export const Dummy = () => {
const [data, setData] = useState("Test");
return <>{data}</>
}
I want to know how can I solve this issue
You can't use React hook outside of main function. I am pretty sure you put const Dummy() function outside the main function.
import React,{useState} from "react";
export const Dummy = () => {
const [data, setData] = useState("Test");
return <>{data}</>
}
export default function SomeMainFunction() {
...
}
please change above function as below.
import React,{useState} from "react";
export default function SomeMainFunction() {
export const Dummy = () => {
const [data, setData] = useState("Test");
return <>{data}</>
}
...
}
Related
Everytime i use react bootstrap component it doesn't work and gives invalid hook error.
import React, { useState } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
function ShowProducts() {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div className="App">
<p>Working</p>
<Button>Test</Button>
</div>
)
}
export default ShowProducts;
it gives following in console:
react.development.js:209 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:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
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.
In my project I have the index.tsx calling the App.tsx that uses a UserProvider and AuthProvider. I received an invalid call error from inside the UserProvider because I'm using the useState hook.
This problem occurs also if I create a custom hook, I can't use any hook inside the custom hook.
This is my index.tsx:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
//import './commons/global.css';
import reportWebVitals from './reportWebVitals';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root'),
);
reportWebVitals();
This is my App.tsx:
import { useEffect, useState } from 'react';
import { BrowserRouter, useNavigate } from 'react-router-dom';
import {
AuthService,
AuthProvider,
useAuth,
useLogin,
UserProvider,
} from 'reactjs-oauth2-pkce-provider';
import './index.css';
import Routes from './routes';
const authService = new AuthService({...});
const App = () => {
const [user, setUser] = useState({});
const [loading, setLoading] = useState(true);
// for every refresh of the page, check if there is a user in localStorage
useEffect(() => {
const user = localStorage.getItem('user');
if (user) {
setUser(JSON.parse(user));
}
setLoading(false);
}, []);
return (
<UserProvider>
<AuthProvider authService={authService}>
<Routes />
</AuthProvider>
</UserProvider>
);
};
export default App;
This is my UserProvider.tsx:
import React, { ReactElement, useState } from 'react';
import { UserContext } from '#app/application/UserContext';
export const UserProvider = ({ children }: { children: ReactElement }) => {
const [user, setUser] = useState({});
console.log(user);
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
};
There is no function that is calling the hook outside a functional component (there is?)!
A important thing to note is that the UserProvider.tsx is part of a separate package for authentication that I'm building and importing from it with yarn link.
I already tested to install directly from github without success.
I already tested declaring the function with React.FC type but the result is the same.
The understanding that I have from it until now is that the react do not know that the App() function is a functional component and the call for UserProvider is inside a functional component.
I'm expecting that I can use hooks inside the provider, to work useState, useEffect, useNavigate.
The error message is telling you that your error is in fact originating from your Hook call in UserProvider.tsx at line 5.
When you don't import components traditionally as react Components such as
<Component/>
and instead import them as a function such as
return component();
or as in your case, importing them as a wrapper:
<Component>Parent</Component>
Then React hooks won't work.
I would recommend using Redux for logins or the useContext Hook to manage logins if you can't figure out the Traditional React Hooks way of managing this. useContext is also a React hook which would cause the same error if implemented in a similar way, but Redux wouldn't cause you to adhere to any of these strict React rules..
There's a lot of existing Login Templates on Github, including ones with Redux. Implementing Logins is the most boring and tedious process I've dealt with, which can usually take extremely long. Traditionally I use App Skeletons with login capabilities already implemented, and then add all the fun code over, but try the suggestions I mentioned.
The error message that you posted contains three possible reasons for the error. Based on the information that you've provided, reason #3 seems like the most likely culprit.
You might have mismatching versions of React and React DOM.
You might be breaking the Rules of Hooks.
You might have more than one copy of React in the same app.
I don't see anywhere in your code, at least not in what you've posted, that you are violating the rules of hooks.
It's what you've said here that jumps out:
A important thing to note is that the UserProvider.tsx is part of a separate package for authentication that I'm building and importing from it with yarn link.
I suspect that your reactjs-oauth2-pkce-provider package is declaring react in the dependencies rather than the peerDependencies. This would cause you to have two copies of react -- one from the package and another from your main app. If these two versions don't match, you could be dealing with reason #1 as well.
The "Duplicate React" section of the docs contains a few checks that you can do to confirm that you do in fact have two copies of React.
If you see more than one React, you’ll need to figure out why this happens and fix your dependency tree. For example, maybe a library you’re using incorrectly specifies react as a dependency (rather than a peer dependency).
Since this is a package that you created and control, you can fix the problem at its root. the solution is to remove react from the dependencies array in your package's package.json and move it to the peerDependencies array instead. You may also need it in the devDependencies. Here is a good example.
Next13 was released a week ago, and I am trying to migrate a next12 app to a next13.
I want to use server-side components as much as possible, but I can't seem to use
import { createContext } from 'react';
in any server component.
I am getting this error:
Server Error
Error:
You're importing a component that needs createContext. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
,----
1 | import { createContext } from 'react';
: ^^^^^^^^^^^^^
`----
Maybe one of these should be marked as a client entry with "use client":
Is there an alternative here or do I have to resort to prop drilling to get server-side rendering?
It seems like I can use createServerContext
import { createServerContext } from 'react';
If you're using Typescript and React 18, you'll also need to add "types": ["react/next"] to your tsconfig.json compiler options, since this is a not-yet-stable function.
This is a new feature from React's SSR to recognize whether a component is client-side or server-side. In your case, createContext is only available on the client side.
If you only use this component for client-side, you can define 'use client'; on top of the component.
'use client';
import { createContext } from 'react';
You can check this Next.js document and this React RFC for the details
According to Next.js 13 beta documentation, you cannot use context in Server Components:
In Next.js 13, context is fully supported within Client Components, but it cannot be created or consumed directly within Server Components. This is because Server Components have no React state (since they're not interactive), and context is primarily used for rerendering interactive components deep in the tree after some React state has been updated
However, there are alternative ways to handle data in the new approach, depending on your case. F.e. if you fetched the data from the server in a parent component and then passed it down the tree through Context, you can now fetch the data directly in all the components that depend on this data. React 18 will dedupe (de-duplicate) the fetches, so there are no unnecessary requests.
There are more alternatives in the documentation.
I've made a tiny package to handle context in server components, works with latest next.js, it's called server-only-context:
https://www.npmjs.com/package/server-only-context
Usage:
import serverContext from 'server-only-context';
export const [getLocale, setLocale] = serverContext('en')
export const [getUserId, setUserId] = serverContext('')
import { setLocale, setUserId } from '#/context'
export default function UserPage({ params: { locale, userId } }) {
setLocale(locale)
setUserId(userId)
return <MyComponent/>
}
import { getLocale, getUserId } from '#/context'
export default function MyComponent() {
const locale = getLocale()
const userId = getUserId()
return (
<div>
Hello {userId}! Locale is {locale}.
</div>
)
}
This is the code for it, it's really simple:
import 'server-only'
import { cache } from 'react'
export default <T>(defaultValue: T): [() => T, (v: T) => void] => {
const getRef = cache(() => ({ current: defaultValue }))
const getValue = (): T => getRef().current
const setValue = (value: T) => {
getRef().current = value
}
return [getValue, setValue]
}
in my company we are using ReactJS to develop our website. We also have legacy code in jQuery (I know, we are trying to change everything to React). My problem is that we have some global functions that we have to pass throughout all the component tree. For instance, we have a control function that we have to pass throughout 8 components, but only the last one actually calls it.
So, I wonder if there's a way to avoid this problem. Another problem is that we have several react trees on the page, because as I said, we have some legacy code in jQuery. Any ideas/suggestions?
(pls if this question does not belong in this forum let me know)
So you have to create yout context like this:
import React from "react";
const YourContext = React.createContext({ func: null });
export default YourContext ;
then in your parent component you can initialize it and make it available in child components:
import React from "react";
import YourContext from "./YourContext";
const YourParentComponent = () => (
<YourContext.Provider value={{ func: () => {} }}>
....
</YourContext.Provider>
);
and in your child components you can use it:
import React, { useContext } from "react";
import YourContext from "../YourContext";
const YourChildComponent = () => {
const { func } = useContext(YourContext);
Have a look at React Context, it will allow you to pass data through the components tree without passing down the props.
I have a React app in Typescript where I'm trying to use the useState hook in one section and running into trouble. I've reduced the code to the following minimal example:
projects-loader.tsx:
import React from "react";
import { render } from "react-dom";
import MoreProjectsTable from "./components/MoreProjectsTable";
export const projects = (): void => {
const moreProjectsRootEl = document.getElementById(
"react-more-projects-table-root"
);
render(<MoreProjectsTable/>, moreProjectsRootEl);
};
export default projects;
MoreProjectsTable.tsx:
import React, { ReactElement, useState } from "react";
import MoreProjectsRow from "./MoreProjectsRow";
const MoreProjectsTable = (): ReactElement<HTMLElement> => {
useState();
return (<div/>);
};
export default MoreProjectsTable;
But when I call projects() it fails with the error "Hooks can only be called inside the body of a function component."
I understand from research that there are three common reasons for this error:
Violating the Rules Of Hooks
Mismatching versions of React and React-DOM
Multiple copies of React in the same app
Regarding #1, I don't see where I'm violating the Rules Of Hooks--it looks to me like I'm calling useState from the top-level of a function component, all right.
Regarding #2, I verified that React and React-DOM are both v16.8.1.
Regarding #3, I verified via the method described on this page that that is not the case.
Can anyone suggest anything else I might try? I'm very new to React so no suggestion is too obvious.
About one minute after I posted this question, a co-worker figured it out. This app is running on a Web page and, without noticing it, I was loading the top-level script itself from two different places. A variation on scenario #3 then, apparently.