React | Redux Toolkit登出后状态重置异常问题求助
问题描述
这是一款基于Firebase授权、Firestore存储,使用React与Redux Toolkit开发的简易应用。用户登出时,我通过dispatch(removeData())、dispatch(removeUserData())重置所有状态数据,状态虽被重置,但获取数据的函数会立即重新执行并再次填充状态(此时user已为null);而页面刷新后状态为空,无用户信息。
如何实现登出后状态重置且不再执行数据获取函数?我已尝试多种基于if user !== null的判断方案,但均无效。若需更多信息可补充。
获取数据代码
const memoizedFetchEmails = useMemo(() => { async function fetchEmails() { ... } return fetchEmails; }, [user]); const memoizedFetchSubs = useMemo(() => { async function fetchSubs() { ... } return fetchSubs; }, [user]); const memoizedFetchUserData = useMemo(() => { async function fetchUserData() { ... } return fetchUserData; }, [user]); useEffect(() => { if (user !== null) { console.log(user?.uid) memoizedFetchEmails(); memoizedFetchSubs(); memoizedFetchUserData(); } }, [user]);
Redux Actions
removeData(state) { state.emails = []; state.subs = []; }, removeUserData(state) { state.user = null; state.noUser = true; state.themeDark = false; state.userData = null; },
登出函数
const logout = () => { signOut(auth); dispatch(setUser(null)) dispatch(removeData()); dispatch(removeUserData()); navigate("/"); };
Firebase Auth监听代码
useEffect(() => { const unsubscribe = onAuthStateChanged(auth, (currentUser) => { if (currentUser) { dispatch(setUser(currentUser)); } else { dispatch(setUser(null)); } }); return () => { unsubscribe(); }; }, []);
解决方案
核心问题
登出流程中存在重复状态更新+异步请求未中断的问题:
- 手动调用
dispatch(setUser(null))和removeUserData时,已经将user设为null,但signOut(auth)会触发Firebase的onAuthStateChanged回调,再次执行dispatch(setUser(null)),导致user状态两次更新,触发监听user的useEffect两次。 - 即使
user变为null,已经发起的异步数据请求可能仍会继续执行,完成后重新填充状态。
具体修复方案
1. 清理冗余的状态更新
removeUserData已经将state.user设为null,同时signOut(auth)的监听回调也会处理user的重置,手动调用dispatch(setUser(null))属于冗余操作,会导致不必要的状态更新。修改登出函数:
const logout = () => { signOut(auth); dispatch(removeData()); dispatch(removeUserData()); navigate("/"); };
2. 给异步请求加取消机制
使用AbortController中断登出后仍在执行的请求,避免其更新状态:
// 修改数据获取的useEffect useEffect(() => { const controller = new AbortController(); const signal = controller.signal; if (user !== null) { const fetchAllData = async () => { try { // 给每个fetch函数传入signal await memoizedFetchEmails(signal); await memoizedFetchSubs(signal); await memoizedFetchUserData(signal); } catch (err) { if (err.name === 'AbortError') { // 忽略取消请求的错误 return; } // 处理其他错误 console.error('数据获取失败:', err); } }; fetchAllData(); } // 组件卸载或user变化时取消所有请求 return () => controller.abort(); }, [user, memoizedFetchEmails, memoizedFetchSubs, memoizedFetchUserData]);
同时更新memoized的fetch函数,支持接收signal参数(以Firestore为例):
const memoizedFetchEmails = useMemo(() => { async function fetchEmails(signal) { const querySnapshot = await getDocs(collection(db, 'emails'), { signal }); // 处理数据并dispatch更新状态 // ... } return fetchEmails; }, [user]);
3. 利用noUser状态加强判断
你在removeUserData中设置了state.noUser = true,可以在useEffect中同时判断user和noUser,确保只有正常登录状态才执行数据获取:
// 从Redux中取出noUser状态 const { user, noUser } = useSelector(state => state.yourSliceName); useEffect(() => { // 仅当user存在且非登出状态时执行 if (user !== null && !noUser) { memoizedFetchEmails(); memoizedFetchSubs(); memoizedFetchUserData(); } }, [user, noUser, memoizedFetchEmails, memoizedFetchSubs, memoizedFetchUserData]);
4. 优化Auth监听逻辑
避免Auth监听触发不必要的状态更新,只有当Firebase返回的用户与Redux中的用户不一致时才dispatch:
const currentUserFromStore = useSelector(state => state.yourSliceName.user); useEffect(() => { const unsubscribe = onAuthStateChanged(auth, (currentUser) => { if (currentUser !== currentUserFromStore) { dispatch(setUser(currentUser)); } }); return () => unsubscribe(); }, [currentUserFromStore]);
内容的提问来源于stack exchange,提问作者wch
相关产品推荐
相关产品推荐

