有父子两个组件 父子都有一个数组 父组件的数组list更新, 子组件的数组tableData会跟着更新,并显示在表格上.
现在有个新增的按钮, 要求在新增时 先更新一下表格 再插入新的一行. 但是更新表格是在父组件请求,子组件通过useEffect跟着更新,导致新增时插入的一行被覆盖了(useEffect更新的逻辑比插入逻辑晚导致被覆盖),要怎么处理.
// 父组件
const Father = () => {
const [list, setList] = useState([])
const getListData = async () => {
// 请求获取list数据
const res = await getList().catch(() => {})
setList(res.data)
}
return (<Son list={list} getListData={getListData} />)
}
// 子组件
const Son = ({list, getListData}) => {
const [tableData, setTableData] = useState([])
// 当父组件的数据更新时 子组件也跟着更新
useEffect(() => {
setTableData(list)
}, [list])
// 新增
const onAdd = async () => {
// 在新增之前先获取一下最新的表格数据list 调用父组件的请求函数 因为useEffect 当前子组件的tableData也会更新一下
await getListData()
// 给当前组件的表格数据添加一行新增的空行(不改变父组件的数据) 但是由于这里会先于useEffect执行 所以会被覆盖 没有效果
setTableData([{空行数据}, ...tableData])
}
return (
<div>
<Table dataSource={tableData} />
<Button onClick={onAdd}>新增</Button>
</div>
)
}
一个办法是把新增一行的逻辑移到useEffect处,加上判断来执行. 但明显这么写很不友好.
这其实是一个组件设计上的问题,用于渲染的表格数据只需要有一份即可,通常作为根组件的 state 是没问题的。但你这里子组件又维护了一份 state,对于组件的渲染来说形成了一个双数据源,不仅容易造成你描述的这种问题,也无端增加了开发过程的心智负担。
正确的做法是
子组件
直接使用父组件传过来的 list 去渲染,不用再存一个 state,如果存在子组件需要更新父组件的 state 的情况,应该从父组件传一个用于更新的方法给子组件。总之保证
渲染数据的单一性
以及更新数据方式的唯一性
基本上就能避免这种问题的出现比较简单的处理方式如下