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

React函数内useState状态为空 点击删除按钮表格清空问题

问题根因

handleDelete中读取到的list始终为空,是典型的React闭包陷阱:
你定义表格列的useMemo传入了空依赖数组[],意味着首次组件渲染生成的columns配置会被一直缓存复用,不会随状态更新重新生成。而columns里的删除按钮点击事件,绑定的是首次渲染时的handleDelete函数——这个函数闭包捕获的list就是初始状态的空数组,所以后续哪怕接口请求完list已经更新,点击触发的还是旧函数,拿到的永远是空值,过滤后自然会把表格数据清空。
另外你的fetchData函数还有个逻辑问题:setIsLoading(false)写在了Promise链外面,请求刚发出去还没等返回结果,就会直接把加载状态设为false,不符合预期。

修复方法

两种可选方案,选一种即可:

方案1:修正依赖关系

  1. 用useCallback包裹handleDelete,将list加入依赖数组,保证list更新后能拿到最新的函数版本
  2. 将handleDelete加入columns对应的useMemo依赖数组,保证函数更新后列配置能同步更新
  3. 修正fetchData的loading状态逻辑,等请求完成后再关闭加载态

修复后的App组件核心代码:

const App = () => {
  const [list, setList] = useState([]);
  const [isLoading, setIsLoading] = useState(false);

  async function fetchData() {
    setIsLoading(true);
    try {
      const res = await fetch("https://jsonplaceholder.typicode.com/users");
      const json = await res.json();
      setList(json);
    } catch (err) {
      console.error("列表请求失败", err);
    } finally {
      // 请求完成(成功/失败)后再关闭loading
      setIsLoading(false);
    }
  }

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

  // 用useCallback包裹,依赖list
  const handleDelete = useCallback(async (id) => {
    console.log("当前列表数据", list);
    const newList = list.filter((r) => r.id !== id);
    setList(newList);
    // 此处补充调用删除接口的逻辑
  }, [list]);

  const columns = useMemo(
    () => [
      {
        Header: "Name",
        accessor: "name"
      },
      {
        Header: "Email",
        accessor: "email"
      },
      {
        Header: "Action",
        accessor: "action",
        Cell: ({ cell }) => (
          <button
            className="btn btn-link ms-2"
            title="Delete"
            onClick={() => handleDelete(cell.row.original.id)}
          >
            Delete
          </button>
        )
      }
    ],
    [handleDelete] // 新增handleDelete作为依赖
  );
  const data = useMemo(() => [...list], [list]);

  return (
    <AppTable
      columns={columns}
      data={data}
      pageCount="3"
      fetchDataFn={fetchData}
    />
  );
};

方案2:使用函数式状态更新(更简洁)

不需要调整useMemo和useCallback的依赖,修改handleDelete里的setList写法,用函数式更新获取最新的状态值,完全绕开闭包问题:

const handleDelete = async (id) => {
  // 函数式更新的参数永远是最新的state,不受闭包捕获的旧值影响
  setList(prevList => prevList.filter((r) => r.id !== id));
  // 此处补充调用删除接口的逻辑
};

这种写法改动最小,不需要调整memo依赖,也能解决拿到空list的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 17:15:44