使用React+Redux+Firebase开发Todo应用调用useSelector出现无限循环问题
问题根源
你的无限循环由以下几个逻辑共同导致:
getTaskList直接写在组件顶层,没有用useEffect包裹,组件每渲染一次就会调用一次这个函数getTaskList拉取数据后会 dispatch 修改 Redux 中的user状态useSelector监听到state.user变化后会触发组件重新渲染,又回到第一步调用getTaskList,形成死循环- 额外的触发点:你在 reducer 中每次都返回一个新的对象
{task: data},就算数据内容没有变化,新对象的引用地址和旧状态不同,useSelector 默认的浅比较也会认为状态更新,触发重渲染
修复步骤
1. 把获取任务列表的逻辑包裹进 useEffect
删除组件顶层的 getTaskList() 调用,改成只在用户登录状态变化时才拉取数据:
首先导入 useEffect:
import React, { useState, useEffect } from "react";
然后在组件内添加如下代码:
// 只在user变化时触发拉取任务列表 useEffect(() => { // 用户未加载完成不执行 if (loading || !user) return; const getTaskList = async () => { try { const querySnapshot = await getDocs( collection(db, "users", user.uid, "tasks") ); dispatch(taskInfo({ querySnapshot })); } catch (e) { console.log(e); } }; getTaskList(); }, [user, loading, dispatch]);
2. 优化useSelector的比较逻辑(可选,避免不必要的重渲染)
从 react-redux 导入浅比较方法,让useSelector只在内容真正变化时才触发重渲染:
import { useDispatch, useSelector, shallowEqual } from "react-redux"; // 修改useSelector的调用 const tasks = useSelector((state) => state.user, shallowEqual);
3. 修复reducer的状态类型不一致问题
你初始state定义的是数组,但是实际返回的是对象,建议统一类型避免隐式问题:
// 修改UserReducer里的初始状态 initialState: { task: [] },
4. 优化id生成逻辑(非循环相关,避免无效渲染)
不要在组件顶层调用nextId(),每次创建任务的时候再生成id即可:
// 删除顶层的 const htmlId = nextId(); 移到createTask内部 const createTask = async () => { const htmlId = nextId(); const taskRef = doc(db, "users", user.uid, "tasks", htmlId); try { await setDoc( taskRef, { task: newTask, duration: newDuration }, { merge: false } ); // 创建完成后可以主动拉取一次最新列表,或者直接更新redux } catch (e) { console.log(e); } };
内容的提问来源于stack exchange,提问作者Sarthak-bleepboop
相关产品推荐
相关产品推荐

