Home > front end >  React how to set context from a function. Invalid hook call
React how to set context from a function. Invalid hook call

Time:01-18

I'm not able to retrieve a context value from a function not in a Component. I receive following exception:

Uncaught (in promise) 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...

I've declared my context.

export const ErrorContext = createContext({})
export const UseErrorContext = () => useContext(ErrorContext)

Set up a provider within my App.js

<ErrorContext.Provider value={{ errorMessage }}>
</ErrorContext.Provider>

And like to set the value from a function like so. But this results in Exception above. This function is in a seperate file and called from different components.

export const MyFunction = async (id) => {
   const { errorMessage } = UseErrorContext();
   errorMessage = "SOME ERROR MESSAGE";
 }

          

CodePudding user response:

You won't be able to do it this way: You can't call a hook outside the render phase in react.

Besides, even if you call it this way, the assignment is local and wouldn't affect the context value.

To achieve your desired behavior, you need:

  • A state to hold the error message along with a state setter
  • The context value should change each time the error changes
  • You should grab the state setter from a component calling your hook
  • Pass your state setter as a callback to your async function

Something like this:

// context
export let ErrorContext = createContext({})
export let useErrorContext = () => useContext(ErrorContext)

// provider
let [errorMessage, setErrorMessage] = useState();
let value = useMemo(() => ({errorMessage, setErrorMessage}), [errorMessage])
<ErrorContext.Provider value={value}>
  {children}
</ErrorContext.Provider>

// component
let {setErrorMessage} = useErrorContext();


export const myFunction = async (id, setErrorMessage) => {
   setErrorMessage("SOME ERROR MESSAGE");
 }

// later, in an event:
myFunction(id, setErrorMessage);


PS: This solution requires you to do a Provider per error message so you can have multiple, or you should change the abstraction.

Another solution which may seem like a promotion to my own library, but it is not, just to show how easily it solves this:

yarn add -D async-states react-async-states


import {useAsyncState, createSource} from "react-async-states"

// myErrorMessage should be unique, or else the library will warn
let myErrorMessageSource= createSource("myErrorMessage");



// in your component
let {state, setState} = useAsyncState(myErrorMessageSource);
// then use the state.data to display it


// in your async function, and your component would react to it
myErrorMessageSource.setState("SOME ERROR MESSAGE");

This is just a basic use case of the library, please read the docs before opt for it.

CodePudding user response:

as the error says :

Hooks can only be called inside of the body of a function component

what I suggest is to create your custom hook

A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.

now this is possible

export const useMyFunction = async (id) => {
const { errorMessage } = UseErrorContext();
...
}

make sure to only call other Hooks unconditionally at the top level of your custom Hook.

same when calling your custom hook from another component

  • Related