如何修复模拟dispatch时useDispatch React Hook被条件调用的问题
解决React Hook "useDispatch"被条件调用的错误
问题根源
你在组件的props默认值里直接调用了useDispatch():
_useDispatch = useDispatch() // 这里违反了Hook规则
React要求Hook必须在组件顶层、每次渲染都按相同顺序调用,而props默认值的执行时机不确定——当父组件传入_useDispatch时,这个默认值里的Hook调用会被跳过,导致不同渲染中Hook调用顺序不一致,触发报错。
修复步骤
- 修改组件的props默认值:把
useDispatch()改成useDispatch(传递Hook函数本身,不立即调用) - 在组件顶层调用Hook:在函数体内统一调用
_useDispatch()获取dispatch方法
修改后的组件代码:
import { Stage } from "../Stage/Stage"; import { useDispatch, useSelector } from "react-redux"; import { useEffect } from "react"; import { retrieveStageList } from "../../modules/reducer"; import { Process } from "../Process/Process"; export function RenderProcess({ _useSelector = useSelector, _useDispatch = useDispatch, // 只传函数,不调用 _Process = Process, }) { const dispatch = _useDispatch(); // 在组件顶层调用,符合Hook规则 const process = _useSelector((state) => state.renderProcess); const stageList = _useSelector((state) => state.stageList); useEffect(() => { if (process.processId !== null) dispatch(retrieveStageList(process.processId)); }, [dispatch, process.processId]); // 补充依赖,避免旧闭包问题 return ( <> <_Process process={process} /> {stageList?.map((stageInputs, processId) => { return ( <div key={processId}> <Stage stage={stageInputs} /> </div> ); })} </> ); }
额外说明:useEffect的依赖数组要加上dispatch和process.processId,避免因为闭包导致的逻辑错误(React Redux的dispatch本身是稳定的,但显式声明依赖更规范)。
测试代码无需调整
测试中传入的_useDispatch会返回模拟的dispatch函数,组件现在会调用传入的_useDispatch获取dispatch,原测试代码可以正常运行:
import { render } from "@testing-library/react"; import { RenderProcess } from "./RenderProcess"; test("should call dispatch once.", () => { const _useSelector = (fn) => fn({ stageList: [], renderProcess: { processId: "309624b6-9c96-4ba7-8f7e-78831614f685" }, }); const dispatch = jest.fn(); render( <RenderProcess _useSelector={_useSelector} _useDispatch={() => dispatch} _Process={() => {}} /> ); expect(dispatch).toHaveBeenCalledTimes(1); });
这样修改后,不管是正常运行还是测试模拟,都能符合React Hook的规则,解决报错问题。
内容的提问来源于stack exchange,提问作者Callie
相关产品推荐
相关产品推荐

