ReactJS中useReducer如何接收props传入的异步数据作为初始状态
问题根因
useReducer传入的初始状态参数 仅在组件首次挂载阶段计算一次,后续props更新不会自动重新计算初始值,也不会自动同步到reducer维护的内部状态中。- 你当前的执行流存在时序错位:
- 组件首次挂载时,父组件的axios异步请求尚未返回,传入的
products为父组件useState([])定义的初始空数组,因此reducer初始化拿到的data为空值。 - 异步请求完成后,父组件更新
productList状态并向TableSorter传入新的products数据,此时组件已经完成首次挂载,reducer的初始状态不会重新执行赋值逻辑,因此内部data始终为空。 - 目前Table.Body直接遍历props传入的products虽然能渲染内容,但完全绕开了reducer维护的排序逻辑,排序功能完全无法生效。
- 组件首次挂载时,父组件的axios异步请求尚未返回,传入的
修复步骤
1. 扩展reducer的action分支
为reducer新增处理外部数据同步的逻辑,用来接收从props传入的最新产品数据:
function exampleReducer(state, action) { switch (action.type) { // 新增的同步外部数据分支 case 'SET_EXTERNAL_DATA': return { column: null, data: action.payload, direction: null, } case 'CHANGE_SORT': // 原有排序逻辑保持不变 if (state.column === action.column) { return { ...state, data: state.data.slice().reverse(), direction: state.direction === 'ascending' ? 'descending' : 'ascending', } } return { column: action.column, data: _.sortBy(state.data, [action.column]), direction: 'ascending', } default: throw new Error() } }
2. 移除reducer初始状态对props的直接依赖
将useReducer的初始状态改为独立的空结构,不再依赖传入的products做初始化:
function TableSorter({products}) { // 初始状态固定为空结构,避免和props绑定 const initialState = { column: null, data: [], direction: null, } const [state, dispatch] = useReducer(exampleReducer, initialState) const { column, data, direction } = state
3. 通过副作用监听props变化,同步到reducer状态
使用useEffect监听传入的products值变化,当检测到数据更新时,派发对应action把最新数据同步到reducer的内部状态:
// 监听products变化,自动同步到reducer内部 useEffect(() => { // 仅当外部products和内部data引用不一致时才更新,避免死循环 if (products !== state.data) { dispatch({ type: 'SET_EXTERNAL_DATA', payload: products }) } }, [products, state.data])
4. 替换表格渲染的数据源
将Table.Body里直接遍历props的逻辑,改为遍历reducer维护的state.data,让所有排序操作的结果直接作用在渲染的数据源上:
<Table.Body> {data?.map(({name, expiration_date, sku, category, price, shipment_batch}) => ( <Table.Row key={name}> <Table.Cell>{name}</Table.Cell> <Table.Cell>{expiration_date}</Table.Cell> <Table.Cell>{sku}</Table.Cell> <Table.Cell>{category}</Table.Cell> <Table.Cell>{price}</Table.Cell> <Table.Cell>{shipment_batch}</Table.Cell> </Table.Row> ))} </Table.Body>
补充说明:
SET_EXTERNAL_DATA分支里重置了排序相关的column和direction值,是为了当外部传入全新的数据集(比如用户重新搜索拿到的产品列表)时,自动清空之前残留的排序状态,符合通用表格组件的交互预期。
内容的提问来源于stack exchange,提问作者c0caDJ
相关产品推荐
相关产品推荐

