为什么Firebase的onAuthStateChanged回调中Redux状态不会更新?
问题原因
这是React Hooks的经典闭包陷阱问题,触发逻辑如下:
- 你给
useEffect传入了空依赖数组[],所以这个副作用只会在组件首次挂载时执行1次 - 执行时
onAuthStateChanged绑定的回调函数,捕获到的是当前作用域下的authUser变量,也就是初始状态下status为loading的版本 - 后续Redux store中的
authUser更新后,组件会重新渲染生成新的authUser变量,但已经绑定的Firebase回调属于首次渲染的闭包,永远只会读取当时捕获的旧authUser值,所以回调内打印的status永远是loading - 首次两次打印
loading的原因:第一次是组件首次渲染时外部的console.log执行,第二次是Firebase初始状态触发onAuthStateChanged回调,两次拿到的都是初始值,所以输出一致
解决方案
有两种常见的修复方式:
方案1:补充useEffect依赖,状态变更时重新绑定监听
将authUser.status添加到useEffect的依赖数组中,每次依赖变化时会先销毁旧的监听,再绑定新的回调,新回调可以拿到最新的authUser值:
const authUser = useSelector(state => state.authUser) console.log(authUser.status) useEffect(() => { const unsubscribe = onAuthStateChanged(auth, () => { console.log(authUser.status) if (authUser.status === 'loading') { dispatch(updateStatus('loaded')) } return () => unsubscribe() }, [authUser.status, dispatch]);
方案2:用useRef存储最新状态,避开闭包限制
如果不需要频繁重新绑定监听,可以用ref同步最新的authUser值,回调中直接读取ref的current属性即可拿到最新状态:
const authUser = useSelector(state => state.authUser) const authUserRef = useRef(authUser) // 同步最新的authUser到ref useEffect(() => { authUserRef.current = authUser }, [authUser]) console.log(authUser.status) useEffect(() => { const unsubscribe = onAuthStateChanged(auth, () => { console.log(authUserRef.current.status) if (authUserRef.current.status === 'loading') { dispatch(updateStatus('loaded')) } return () => unsubscribe() }, [dispatch]);
内容的提问来源于stack exchange,提问作者kr17
相关产品推荐
相关产品推荐

