4.1 Hooks can only be called inside the body of a function component
原因(三选一):
- 在条件 / 循环 / 嵌套函数里调用 Hook
- 同一项目装了两份 React(检查
npm ls react) - 组件不是 PascalCase(
function home()被当普通函数,Hook 调用不合法)
定位:
cd frontend && npm ls react
若出现两个版本,在 vite.config.ts 加:
resolve: { dedupe: ['react', 'react-dom'] }
4.2 Cannot read properties of undefined (reading 'xxx')
定位:
- 打开 React DevTools,定位到崩溃组件,看 props / state
- 在崩溃前加
console.log看值的来源
修法:
- 加可选链
obj?.xxx是应急,真正要追为什么是 undefined - 是异步还没回来 → 加 loading 守卫
- 是父组件没传 → 父组件改
4.3 Maximum update depth exceeded 无限渲染
根因:useEffect 依赖里放了每次渲染都变的引用(对象字面量、新数组、内联函数)。
定位:
useEffect(() => {
console.log('effect run', deps);
// ...
}, [deps]);
看哪个依赖每次都是"新"的(Object.is 比较)。
修法:
useMemo/useCallback包住依赖- 把对象拆成基础类型(string / number)放进依赖
- 用 ref 替代:不需要触发渲染的值放
useRef
4.4 Warning: Each child in a list should have a unique "key" prop
误区:不能用 index 当 key,尤其列表会排序/删除/插入时。
修法:用数据自身唯一 id(item.id)。
4.5 antd 5 相关
4.5.1 Warning: [antd: Form] Instance created by useForm is not connected to any Form element
<Form form={form}> 没传 form 实例,或 Form 还没挂载就调用 form.setFieldsValue。
修法:在 useEffect 或 onFinish 里调用 form 方法,不要在 render 阶段。
4.5.2 findDOMNode is deprecated in StrictMode
antd 旧版组件在 React 18 严格模式下警告,升级 antd 到 5.13+。
4.5.3 antd v5 CSS-in-JS 闪烁(FOUC)
SSR / Vite 下首屏样式丢失。
解决:
import { StyleProvider } from '@ant-design/cssinjs';
<StyleProvider hashPriority="high">
<App />
</StyleProvider>
4.5.4 Modal / Drawer 关闭时报错 unmounted component
原因:destroyOnClose 未开,内部状态在关闭后继续更新。
修法:<Modal destroyOnClose>。
4.6 Zustand 相关
4.6.1 组件不更新
// ❌ 返回整个对象,引用不变
const state = useStore(s => s);
// ✅ selector,只订阅需要的字段
const user = useStore(s => s.user);
4.6.2 持久化 hydrate 时 undefined
persist 在第一次 render 后才注水,初始值会闪一下。
修法:
const hasHydrated = useStore.persist.hasHydrated();
if (!hasHydrated) return <Loading />;
或用 onRehydrateStorage 回调。
4.6.3 store 在多个 tab 不同步
persist 默认只同步到 localStorage,不跨 tab 通知。要跨 tab 用 storage 事件或 BroadcastChannel。
4.7 react-router v6
4.7.1 useNavigate() may be used only in the context of a <Router> component
某个组件在 <BrowserRouter> 外被渲染(常见于在 main.tsx 之外的工具调用 navigate)。
修法:把跳转改为返回结果,在组件内调用 navigate;或暴露 router 实例。
4.7.2 嵌套路由白屏
父路由忘了写 <Outlet />。检查 ../frontend/src/components/Layout.tsx 是否渲染了 Outlet。
4.7.3 路由切换不重置 state
component 是同一个,React 复用 fiber,内部 state 保留。
修法:
<Routes>
<Route path="/foo/:id" element={<Foo key={location.pathname} />} />
</Routes>
用 key 强制重建组件。
4.7.4 Link 跳转刷新整页
原因:用了 <a href> 而非 <Link to>。
4.8 useEffect 双执行(StrictMode)
React 18 <StrictMode> 下,dev 模式 effect 会跑两次,这是故意的,用来暴露副作用清理问题。
正确做法:写好 cleanup,不要为了不双执行而关 StrictMode。
useEffect(() => {
const id = setTimeout(...);
return () => clearTimeout(id); // 必须 cleanup
}, []);
4.9 Hydration mismatch(SSR 场景)
本项目是纯 CSR,理论上不会遇到。但若引入 Next.js / Remix:
- 服务端和客户端渲染结果不一致(
Date.now()、Math.random()、window) - 修法:把不一致部分用
useEffect推迟到 client 端