【问题标题】:React hook causing rerendering反应钩子导致重新渲染
【发布时间】:2020-01-17 04:32:15
【问题描述】:

我有一个从本地存储读取令牌并从令牌中解码用户声明的钩子。

export const useActiveUser = (): { user: IUserTokenClaims | null } => {
    const [user, setUser] = useState<IUserTokenClaims | null>(null);

    useEffect(() => {
        const token = localStorage.getItem("token");

        if (!token) setUser(null);
        else {
            const claims = jwt.decode(token) as IUserTokenClaims;
            if (!claims.id || !claims.displayName || !claims.role) setUser(null);
            else {
                setUser({
                    id: claims.id,
                    displayName: claims.displayName,
                    role: claims.role
                });
            }
        }

    }, []);

    return {  user };
};

如果我在这样的组件中使用钩子......

export const SomeComponent: React.FC = () => {
    const { user } = useActiveUser();

    console.log(user)

    if (user) {/* Render stuff if user is logged in */}
    else {/* Render stuff if user is not logged in */}
}

日志语句将打印null,并在第二次渲染时注销用户声明。

为什么会渲染两次?

【问题讨论】:

    标签: reactjs typescript jwt


    【解决方案1】:

    useActiveUser 钩子的第一行:

    const [user, setUser] = useState<IUserTokenClaims | null>(null);
    

    user 的初始值设置为null,并且您只需将user 设置为useEffect 钩子中的另一个值,该钩子在挂载(之后)(初始渲染)上运行。因此在第一次渲染时usernull。您可以处理此问题或对代码进行一些修改以避免此问题。

    通常为避免出现奇怪的渲染,您可以添加 isLoading 状态值,但是,该方法最适合必须异步设置的状态值(例如,通过调用 API)。在这种情况下,您可以立即将user 的初始值设置为计算值(因此不需要isLoading)。

    例如,在设置user状态值并使用useMemo钩子之前计算user的值:

    export const useActiveUser = (): { user: IUserTokenClaims | null } => {
        const user = useMemo(() => {
            const token = localStorage.getItem("token");
    
            if (!token) {
                return null;
            } else {
                const claims = jwt.decode(token) as IUserTokenClaims;
                if (!claims.id || !claims.displayName || !claims.role) {
                    return null;
                } else {
                    return {
                        id: claims.id,
                        displayName: claims.displayName,
                        role: claims.role
                    };
                }
            }
        }, []);
    
        return { user };
    };
    

    【讨论】:

    • 啊,我明白了,我没有意识到默认值会导致初始渲染。您的解决方案有效。我现在一起删除了对 setState 的调用并返回 userData。
    • 在使用您提供的解决方案之前,我在我的实现中使用了isLoading,正如您所提到的。只是看起来......错了哈哈。感谢您的帮助。
    • 是的,当然。 isLoading的方式一般只适用于需要异步设置的状态值(例如调用API),但是这里我们可以同步计算,所以没必要!
    【解决方案2】:

    它会重新渲染两次,因为您在 useEffect 挂钩内调用 setUser,这与类组件上的 componentDidMount 生命周期方法相同。

    1. 第一次渲染
    2. user 值为 null
    3. 组件已安装,useEffect 启动。
    4. 从本地存储解析用户
    5. 设置解析的用户
    6. 触发第二次重新渲染

    【讨论】:

      猜你喜欢
      • 2021-04-23
      • 1970-01-01
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-23
      • 1970-01-01
      相关资源
      最近更新 更多