如何在遵守钩子规则的同时正确使用 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: 修改了帖子,以便关于钩子的唯一未决问题位于顶部。
【问题讨论】:
是什么渲染了您的<LoginForm />
?这将有助于解释您看到的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 而不破坏钩子规则?