如何在遵守钩子规则的同时正确使用 useRouter?

Posted

技术标签:

【中文标题】如何在遵守钩子规则的同时正确使用 useRouter?【英文标题】:How to use useRouter properly while staying within the rules of hook? 【发布时间】:2021-10-19 19:49:34 【问题描述】:

TLDR / 开放式问题:

function LoginForm() 
    const router = useRouter();

    async function submitHandler(event) 
        const found = await tryToLogin();
        if (found) 
            // useRouter().push("/profile");              // <--- line 1
            // router.push("/profile");                   // <--- line 2
        
    
    
    return (
        <>
            <form onSubmit=submitHandler>
                <inputComponent for username>
                <inputComponent for password>
                <button>Login</button>
            </form>
        </>
    );

当第 1 行未注释并加载页面/组件时,我收到 Invalid hook call 错误,谈论违反 Hook 规则。这对我来说很有意义,因为钩子需要在***函数中调用。

但是,当第 2 行未注释并运行(而不是第 1 行)时,不会出现错误。为什么?这怎么不违反规定?显然,将函数的引用存储在“路由器”变量中以某种方式绕过了规则,所以通过在顶层调用 useRouter() 并将其设置为变量,当我通过访问它时不再“调用”它第 2 行的变量?

以上代码是登录页面调用的组件的一部分。页面的 js 文件中的代码检查用户是否已经登录。如果是,它只是重定向到另一个页面。如果用户没有登录,它会加载上面的 LoginForm。在登录表单组件中,用户输入用户名/密码组合,然后点击提交按钮。这就是上面的 submitHandler 函数运行的时候。 正如预期的那样,我的代码正在运行并且工作正常。我只是想了解为什么第 1 行会导致违规,而第 2 行不会。

原始问题

场景:我正在尝试使用 useRouter 在成功登录后从登录页面移动到个人资料页面。如果没有,它会停留在页面上并显示错误等...

现在,我的工作正常,但我试图改进代码并遇到了我无法解释的混乱情况。我主要是后端工程师,所以还在学习 React,因此不确定发生了什么......

请在我解释我遇到的场景时与我交谈。我有以下代码(为清楚起见,仅指出要点):

function LoginForm() 
    const router = useRouter();
    const [userFound, setUserFound] = useState(false);

    console.log("Alpha");                     // Alpha

    useEffect( () => 
        console.log("Bravo");                 // Bravo

        if (userFound) 
            console.log("Charlie");           // Charlie
            router.push("/profile");
        

        return () => 
            console.log("Zulu");              // Zulu
        ;
    , [userFound]);

    async function submitHandler(event) 
        console.log("Delta");                 // Delta
        const found = await tryToLogin();
        if (found) 
            console.log("Epsilon");           // Epsilon
            // useRouter().push("/profile");              // <--- line 1
            // router.push("/profile");                   // <--- line 2
            // setUserFound(true);                        // <--- line 3
            console.log("Fulcrum");           // Fulcrum
        
    

只有第 1 行未注释并正在运行:当我登录时,它会出错并抱怨 Invalid hook call。违反钩子规则等......这对我来说很有意义。您需要在 topLevel 中调用 useRouter。

只有第 2 行未注释且正在运行:此代码运行良好。当我登录时,找到了用户,并将我发送到个人资料页面。我也收到以下控制台消息链:

Alpha Bravo (first pass)
(submit clicked)
Delta Alpha Zulu Epsilon Fulcrum

问题 1: 这怎么不违反规则?显然,将函数的引用存储在“路由器”变量中以某种方式绕过了规则,所以通过在顶层调用 useRouter() 并将其设置为变量,当我通过访问它时不再“调用”它第 2 行的变量?

问题 2: 为什么我看到 ZULU 领先于 EPSILON 和 FULCRUM ?我怀疑它是某种形式的反应钩子链接的东西,但我不太确定,也不明白。有人可以解释或指出一个很好的资源吗?

只有第 3 行未注释且正在运行:现在,事情变得有趣了。根据阅读,这是理想的编码方式。提交处理程序进行状态更改,而 useEffect 作用于状态更改并执行操作。但是,这不会将我带到个人资料页面。相反,它将我带到主页(“/”),我不确定它为什么或如何做到这一点!甚至控制台日志也与运行第 2 行时相同。

Alpha Bravo (first pass)
(submit clicked)
Delta Alpha Zulu Epsilon Fulcrum

问题 3: 为什么第 3 行没有按预期工作?还是我的期望错了?我的期望(或理解):如果 found 为真,那么我们记录“epsilon”,将“userFound”状态设置为真,并记录“支点”。由于状态更改,这将触发组件的重新传递。由于 'userFound' 是一个依赖项,因此会触发 useEffect。它会记录'charlie',然后路由推送'/profile'。这将触发该组件的卸载,这应该会触发清理并记录“Zulu”。

免责声明:

    正如我所提到的,我仍在学习反应。因此,我的理解在这里可能非常错误,如果您指出如何/为什么,我将不胜感激。 抱歉问题标题。我不太确定如何正确地构图。

编辑 1: 感谢@rossAllen 下面的 cmets,我已经注释掉了问题 2 和 3。我在 nextjs 页面中调用 LoginForm,其中还有一些其他钩子(useSession 和 useEffect)。 看起来,当我在等待“tryToLogin”代码返回时,useSession 实际上赢得了竞争条件并获得了会话/用户,这会触发页面的渲染和 useEffects 首先触发。这会导致 LoginForm 卸载,从而将扳手扔到整个模型中。

编辑 2: 修改了帖子,以便关于钩子的唯一未决问题位于顶部。

【问题讨论】:

是什么渲染了您的&lt;LoginForm /&gt;?这将有助于解释您看到的console.log 调用的具体顺序。 @RossAllen - 啊哈。谢谢你。这完全有道理。我忘了去往上看一层。我在 nextjs 页面中调用它,它实际上解释并回答了问题 2 和问题 3(排序)。谢谢你。让我相应地更新我的问题。 你能粘贴更多呈现这个组件的代码吗?如果没有该信息,则无法回答其余问题。 嗨@RossAllen。我已经用 TLDR 和开放式问题更新了这个问题。我试图发布我认为可能与关于钩子规则的最后一个问题相关的内容。我可能是错的,所以让我知道是否需要更多。然而,我怀疑这可能更像是一种 javascript / react / 基本的工作方式...... 【参考方案1】:

在 props const foo=(history)=> 中使用 history,然后使用 history.push('/dashboard') 推送到您的目标路线。

【讨论】:

以上是关于如何在遵守钩子规则的同时正确使用 useRouter?的主要内容,如果未能解决你的问题,请参考以下文章

使用 react-navigation 中的 useRoute 进行 Jest 单元测试

你如何在 React 中调用 useContext 而不破坏钩子规则?

如何遵守 Sonar 的规则“应该使用 SQL 绑定机制”

如何在类组件中使用 next.js 中的“useRouter()”?

反应钩子 useCallback 与循环内的参数

如何在 React Router 6 中使用带有 useRoutes 的索引路由