React函数内useState状态为空 点击删除按钮表格清空问题
问题根因
handleDelete中读取到的list始终为空,是典型的React闭包陷阱:
你定义表格列的useMemo传入了空依赖数组[],意味着首次组件渲染生成的columns配置会被一直缓存复用,不会随状态更新重新生成。而columns里的删除按钮点击事件,绑定的是首次渲染时的handleDelete函数——这个函数闭包捕获的list就是初始状态的空数组,所以后续哪怕接口请求完list已经更新,点击触发的还是旧函数,拿到的永远是空值,过滤后自然会把表格数据清空。
另外你的fetchData函数还有个逻辑问题:setIsLoading(false)写在了Promise链外面,请求刚发出去还没等返回结果,就会直接把加载状态设为false,不符合预期。
修复方法
两种可选方案,选一种即可:
方案1:修正依赖关系
- 用
useCallback包裹handleDelete,将list加入依赖数组,保证list更新后能拿到最新的函数版本 - 将
handleDelete加入columns对应的useMemo依赖数组,保证函数更新后列配置能同步更新 - 修正
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
相关产品推荐
相关产品推荐

