Home > Mobile >  React with TypeScript - React has detected a change in the order of Hooks called by ComponentName
React with TypeScript - React has detected a change in the order of Hooks called by ComponentName

Time:05-08

I am working with a project with users. Right now I am working with the UserProfile

This is the error I am recieving.

React has detected a change in the order of Hooks called by UserProfile

   Previous render            Next render
       ------------------------------------------------------
    1. useState                   useState
    2. useContext                 useContext
    3. useEffect                  useEffect
    4. undefined                  useContext

Let me show some code of the UserProfile component.

export const UserProfile = () => {
    document.title = `${title} - My Profile`;
    const [profile, setProfile] = useState<UserDetails>();

    const {claims} = useContext(AuthContext);

    const getUserEmail = (): string => {
        return claims.filter(x => x.name === "email")[0]?.value.toString();
    }

    useEffect(() => {
        axios.get(`${urlAuth}?userName=${getUserEmail()}`)
            .then((response: AxiosResponse<UserDetails>) => {
                setProfile(response.data);
            })
    }, [getUserEmail]);

    return (
        profile ? 
        <article>
            <h1>This profile belongs to {UserName()}</h1>
            <h2>{profile.name}</h2>
        </article>
        : <div>Loading...</div>
    )
}

I get a warning at the getUserEmail function,

It says

    The 'getUserEmail' function makes the dependencies of useEffect Hook (at line 26) change on every render. 
Move it inside the useEffect callback. 
Alternatively, wrap the definition of 'getUserEmail' in its own useCallback() Hook.

I am not sure on how this should be done. Any ideas on what I could do?

Thanks

CodePudding user response:

Wrap getUserEmail's value in a useCallback. On every render, getUserEmail essentially becomes a 'new' function.

When there's a function in the deps array of a useEffect or other such hooks, React checks it by reference. Since each component function execution/rerender leads to the creation of a new function, your useEffect hook will actually run every single time, sending you into a re-render loop (because it'll run the useEffect, update the state with setProfile, which in turn will trigger another execution, where getUserEmail is different again, leading to the useEffect to run again and so on).

const getUserEmail = useCallback((): string => {
        return claims.filter(x => x.name === "email")[0]?.value.toString();
    }, [claims]);

This should give you a memoized callback that will only be recreated if claims changes. Since claims comes from your context, this should be safe as a dependency.

CodePudding user response:

The reason why you are getting error about order of hooks is I think because of this:

  profile ? 
        <article>
            <h1>This profile belongs to {UserName()}</h1>
            <h2>{profile.name}</h2>
        </article>
        : <div>Loading...</div>

If UserName is a component you should not call it as function, rather as element <UserName/>. When you call it as function react thinks some of the hooks which you call inside it belong to the parent component - this combined with condition profile ? could give you the error.

  • Related