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

Material UI Table setState后数据不更新问题求助

Material UI Table 动态加载Firestore数据不渲染问题

问题现象

静态传递rows数据时,Material UI Table可正常显示;但通过useEffect在页面加载时从Firestore获取数据、setState更新rows后,表格始终为空。控制台无报错,且能打印出正确的rows数据,尝试两种动态写法均无效。

静态可行代码

function Notice () {
  return (
    <div>
      <Table rows= {[{Title: "ABC", A: 1, B: 2, C: 3, D: 4}]} />
    </div>
  );
}

动态无效代码

写法1

function Notice() {
  const [rows, setRows] = useState([]);
  const loadRows = () => {
    getDocs(collection(db, "notice"))
      .then((querySnapshot) => {
        const tempRows = [];
        querySnapshot.forEach((doc) => {
          var fields = doc.data();
          tempRows.push({Title: fields.subject, A: 1, B: 2, C: 3, D: 4});
        });
        setRows(tempRows);
      })
      .catch((error) => {
        console.log(error);
      });
  };
  useEffect(() => {
    loadRows();
  }, []);

  return (
    <div>
      <Table rows={rows} />
    </div>
  );
}

写法2

function Notice() {
  const [rows, setRows] = useState([]);
  const [newProp, setNewProp] = useState([]);

  const loadRows = () => {
    getDocs(collection(db, "notice"))
      .then((querySnapshot) => {
        const tempRows = [];
        querySnapshot.forEach((doc) => {
          var fields = doc.data();
          tempRows.push({Title: fields.subject, A: 1, B: 2, C: 3, D: 4});
        });
        setRows(tempRows);
      })
      .catch((error) => {
        console.log(error);
      });
  };
  useEffect(() => {
    loadRows();
  }, []);

  useEffect(() => {
    console.log(rows);
    setNewProp(rows);
  }, [rows]);

  return (
    <div>
      <Table rows={newProp} />
    </div>
  );
}

排查与解决方法

1. 确保每行有唯一稳定的key

Material UI Table渲染行时依赖唯一key识别组件身份,动态数据若用索引作为key,易因数据变化导致渲染异常。建议用Firestore文档的唯一ID作为key:

修改数据获取逻辑,给每行加入id字段:

const loadRows = () => {
  getDocs(collection(db, "notice"))
    .then((querySnapshot) => {
      const tempRows = [];
      querySnapshot.forEach((doc) => {
        const fields = doc.data();
        tempRows.push({
          id: doc.id, // 加入Firestore文档ID作为唯一标识
          Title: fields.subject || '无标题', // 处理subject为空的边界情况
          A: 1,
          B: 2,
          C: 3,
          D: 4
        });
      });
      setRows(tempRows);
    })
    .catch((error) => {
      console.log(error);
    });
};

同时在Table组件内部的行渲染中,使用这个id作为key:

// Table组件内部示例
<TableBody>
  {rows.map((row) => (
    <TableRow key={row.id}> {/* 使用唯一id而非索引 */}
      <TableCell>{row.Title}</TableCell>
      <TableCell>{row.A}</TableCell>
      <TableCell>{row.B}</TableCell>
      <TableCell>{row.C}</TableCell>
      <TableCell>{row.D}</TableCell>
    </TableRow>
  ))}
</TableBody>

2. 验证数据结构一致性

确认动态生成的rows数组每个对象的字段与静态数据完全一致,比如Title字段是否存在且不为undefined。可在setRows前打印tempRows,检查每个对象的属性是否符合预期:

console.log('生成的tempRows:', tempRows);
setRows(tempRows);

3. 简化状态传递

写法2中新增newProp状态属于冗余操作,会增加状态更新复杂度,直接传递rows即可,减少不必要的状态层级。

4. 添加加载状态(可选)

为避免初始空数组导致的视觉问题,可添加加载状态提示:

function Notice() {
  const [rows, setRows] = useState([]);
  const [loading, setLoading] = useState(true); // 新增加载状态

  const loadRows = () => {
    getDocs(collection(db, "notice"))
      .then((querySnapshot) => {
        const tempRows = [];
        querySnapshot.forEach((doc) => {
          const fields = doc.data();
          tempRows.push({
            id: doc.id,
            Title: fields.subject || '无标题',
            A: 1,
            B: 2,
            C: 3,
            D: 4
          });
        });
        setRows(tempRows);
        setLoading(false); // 数据加载完成后关闭加载状态
      })
      .catch((error) => {
        console.log(error);
        setLoading(false); // 出错也关闭加载状态
      });
  };

  useEffect(() => {
    loadRows();
  }, []);

  return (
    <div>
      {loading ? <div>加载中...</div> : <Table rows={rows} />}
    </div>
  );
}

内容的提问来源于stack exchange,提问作者amren

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 09:53:11