React with TypeScript - React 检测到 ComponentName 调用的 Hooks 的顺序发生了变化

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

我正在与用户一起处理一个项目。现在我正在使用 UserProfile

这是我收到的错误。

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

让我展示一些 UserProfile 组件的代码。

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>
    )
}

我在 getUserEmail 函数中收到警告,

它说

    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.

我不确定该怎么做。 关于我能做什么有什么想法吗?

谢谢

将 getUserEmail 的值包装在 useCallback 中。 在每次渲染时,getUserEmail 本质上变成了一个 'new' 函数。

当 useEffect 或其他类似钩子的 deps 数组中有一个函数时,React 通过引用检查它。由于每个组件函数 execution/rerender 都会导致创建一个新函数,因此您的 useEffect 挂钩实际上每次都会 运行,将您送入 re-render 循环(因为它会 运行 useEffect,用setProfile更新状态,这又会触发另一次执行,其中getUserEmail再次不同,导致useEffect再次运行等等)。

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

这应该会为您提供一个记忆回调,只有在声明发生变化时才会重新创建。由于声明来自您的上下文,因此作为依赖项应该是安全的。

我认为你收到有关钩子顺序错误的原因是:

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

如果 UserName 是一个组件,您不应将其作为函数调用,而应作为元素 <UserName/> 调用。当你调用它作为函数时,react 认为你在它内部调用的一些钩子属于父组件 - 这与条件 profile ? 结合可能会给你错误。