结合useAuthProvider使用useCallback的作用是什么?如何避免useEffect重复执行?
问题解答
关于react-admin源码中useCallback的作用说明
这个useCallback并不是无效的。react-admin官方的useAuthProvider返回的authProvider实例取自顶层AuthContext,是你初始化<Admin>组件时传入的固定实例,除非你手动动态更换authProvider配置,否则它的引用永远不会改变。因此这个useCallback能保证返回的getPermissions函数引用稳定,避免下游组件用到这个钩子时出现不必要的重渲染。
你遇到的问题根源
你当前场景下useAuthProvider每次渲染都返回新的对象/函数实例,导致依赖它的useEffect、useCallback每次都刷新,大概率是两种情况:
- 你对
useAuthProvider做了二次封装,封装逻辑中每次渲染都生成了新的对象/函数实例 - 你使用的非官方修改版
useAuthProvider没有对返回值做引用稳定处理
可行的解决方案
方案1:从根源修复useAuthProvider的引用稳定性(最优)
如果是你自己封装的useAuthProvider,给返回值加useMemo缓存:
// useAuthProvider 内部实现调整 const useAuthProvider = () => { // 内部原有逻辑,比如获取登录态、请求方法等 const getRoles = useCallback(async () => { // 原有getRoles实现 }, [/* 内部依赖 */]) // 用useMemo缓存返回的对象,依赖不变则返回同一个引用 return useMemo(() => ({ getRoles, // 其他暴露的方法 }), [getRoles /* 其他方法依赖 */]) }
修复后你原来的useEffect、useCallback写法都会正常工作,不会重复触发。
方案2:用useRef存最新方法实例(不需要修改上层钩子实现)
如果你没办法修改useAuthProvider的实现,可以用useRef绕开依赖检查,同时避免闭包问题:
const authProvider = useAuthProvider(); const [loading, setLoading] = useState(true); const [loaded, setLoaded] = useState(false); const [roles, setRoles] = useState([]); // 用ref存最新的getRoles引用 const getRolesRef = useRef(authProvider.getRoles); // 同步最新的方法到ref中 useEffect(() => { getRolesRef.current = authProvider.getRoles; }, [authProvider]) // 业务逻辑useEffect不需要依赖方法实例 useEffect(() => { getRolesRef.current() .then((data) => { setRoles(data); setLoaded(true); }) .catch((err) => { console.error(err); }) .finally(() => { setLoading(false); }); }, []);
方案3:手动忽略依赖检查(仅适用于确定authProvider永远不会变更的场景)
如果你确认业务中永远不会动态更换authProvider,可以直接给useEffect加注释忽略ESLint的依赖检查:
useEffect(() => { authProvider() .then((data) => { setRoles(data); setLoaded(true); }) .catch((err) => { console.error(err); }) .finally(() => { setLoading(false); }); // eslint-disable-next-line react-hooks/exhaustive-deps }, []);
内容的提问来源于stack exchange,提问作者Jeremy Richardson
相关产品推荐
相关产品推荐

