You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 09:54:03