React渲染组件前使用useNavigate()结合useState报错问题排查
问题根因
- 你最开始遇到的
Cannot update a component ('BrowserRouter') while rendering a different component ('EditNote')警告,和NoteForm内部的useState完全无关。问题出在你直接在组件渲染阶段调用了navigate():navigate()会触发路由根组件BrowserRouter的内部状态更新,React严格禁止在一个组件的渲染执行过程中直接修改其他组件的状态,因此抛出该警告。 - 你把重定向逻辑移到
useEffect后出现的Cannot read properties of undefined (reading 'me')报错,是因为组件首次挂载时useEffect会先执行一次,此时两个GraphQL请求还在加载中,userData、noteData都还是undefined,直接读取深层属性自然报错。这个问题不需要合并两个请求解决,只要在副作用里增加数据就绪的判断即可。 - 原代码还有个隐藏bug:错误处理分支写的是
${error.message},但你根本没定义error变量,实际捕获到的错误存在noteError和userError里,运行到错误分支时会直接触发变量未定义的报错。
修复方案
你只需要修改EditNote组件即可,NoteForm组件的写法没有问题,不需要调整。修复后的完整代码如下:
import React, { useEffect } from "react"; import { useMutation, useQuery } from "@apollo/client"; import { useNavigate, useParams } from "react-router-dom"; import NoteForm from "../components/NoteForm"; import { GET_NOTE, GET_ME } from "../gql/query"; import { EDIT_NOTE } from "../gql/mutation"; const EditNote = () => { const navigate = useNavigate(); const params = useParams(); const id = params.id; const [editNote] = useMutation(EDIT_NOTE, { variables: { id }, onCompleted: () => { navigate(`/note/${id}`, { replace: true }); } }); const { loading: noteLoading, error: noteError, data: noteData } = useQuery(GET_NOTE, { variables: { id } }); const { loading: userLoading, error: userError, data: userData } = useQuery(GET_ME); // 权限判断放到useEffect中,等数据就绪后再执行 useEffect(() => { // 先校验数据是否返回,避免读取undefined属性 if (!userData?.me || !noteData?.note) return; if (userData.me.id !== noteData.note.author.id) { navigate(`/note/${id}`, { replace: true }); } }, [userData, noteData, id, navigate]); if (noteLoading || userLoading) return 'Loading...'; // 修复原代码error变量未定义的bug if (noteError || userError) return `Error! ${noteError?.message || userError?.message}`; return <NoteForm content={noteData.note.content} action={editNote} />; }; export default EditNote;
关键修改说明
- 所有会触发外部状态更新的操作(比如路由跳转、全局状态修改),都不能直接写在组件渲染主流程里,必须放到
useEffect、事件回调这类渲染提交完成后才执行的逻辑中。 - 涉及异步请求数据的判断逻辑,必须先做空值校验,确认数据存在后再读取深层属性,用可选链
?.可以大幅简化空值判断的写法。 useEffect的依赖数组要把内部用到的所有外部变量都补上,避免闭包陈旧值问题。
内容的提问来源于stack exchange,提问作者Alexey Kanarskiy
相关产品推荐
相关产品推荐

