Recoil中组件外调用useRecoilValue触发Hook报错如何解决
问题说明
需要基于多个Recoil状态值组装生成JSON文件,编写了批量获取Recoil值并组装JSON的工具函数,在React组件内调用该函数时,触发useRecoilValue无效调用的报错。
已排除两种不接受的实现方案:
- 将工具函数整体移入组件内部:涉及取值数量过多,会造成组件代码冗余杂乱
- 将所有Recoil值作为参数传入工具函数:传参过程同样存在代码冗余问题
报错复现
问题代码如下:
const ReactCom = () => { getJson(); // ....其余组件逻辑 } const getJson = () => { let jsonFile = useRecoilValue(someAtom); return jsonFile; }
触发的报错信息:
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks 3. You might have more than one copy of React in the same app
报错核心原因是useRecoilValue属于React Hook,仅允许在React函数组件顶层、自定义Hook顶层调用,普通工具函数不属于Hook合法调用上下文,哪怕函数是在组件内部执行,也会违反Hook调用规则。
可行实现方案
方案1:封装自定义Hook
将工具函数改造为以use开头命名的自定义Hook,既符合Hook调用规则,又能把所有组装逻辑抽离到单独文件和业务组件解耦,不需要手动传入任何Recoil状态值。
示例代码:
// 可单独存放在hooks/useAssembleJson.js文件中,和业务组件完全隔离 const useAssembleJson = () => { // 所有需要的Recoil值都可以在自定义Hook内直接调用获取,无数量限制 const baseInfo = useRecoilValue(someAtom); const config1 = useRecoilValue(configAtom1); const config2 = useRecoilValue(configAtom2); const listData = useRecoilValue(listAtom); // 所有JSON组装逻辑全部放在Hook内部完成 const jsonFile = { base: baseInfo, config: { ...config1, ...config2 }, list: listData // 其余组装逻辑 } return jsonFile; } // 业务组件内调用非常简洁,无冗余代码 const ReactCom = () => { const jsonFile = useAssembleJson(); // 后续直接使用jsonFile即可 // ....其余组件逻辑 }
方案2:定义Recoil Selector派生状态
如果组装出的JSON完全由Recoil状态派生而来,不需要依赖组件内部的其他临时变量,可以直接把组装逻辑封装为Recoil selector,作为全局派生状态管理,Recoil会自动做依赖收集和缓存,依赖的状态更新时派生的JSON会自动同步更新,同样完全和业务组件解耦。
示例代码:
// 可单独存放在store/selectors/jsonFileSelector.js文件中 export const jsonFileSelector = selector({ key: 'jsonFileSelector', get: ({get}) => { // 通过selector内置的get方法获取所有依赖的Recoil状态,不需要调用Hook const baseInfo = get(someAtom); const config1 = get(configAtom1); const config2 = get(configAtom2); const listData = get(listAtom); // 直接完成JSON组装 return { base: baseInfo, config: { ...config1, ...config2 }, list: listData // 其余组装逻辑 } } }) // 业务组件内直接取值即可 const ReactCom = () => { const jsonFile = useRecoilValue(jsonFileSelector); // 后续直接使用jsonFile即可 // ....其余组件逻辑 }
两种方案都不需要把组装逻辑堆在业务组件内,也不需要手动给工具函数传大量Recoil状态参数,完全符合React和Recoil的调用规范。
内容的提问来源于stack exchange,提问作者Dvir Naim
相关产品推荐
相关产品推荐

