React子组件调用父组件排序方法后状态不更新问题排查
问题描述
父组件使用自定义排序函数dynamicSort对对象数组排序,点击子组件表头触发排序时,父组件的listItems状态未更新,但改用slice方法时状态能正常更新。已确认排序函数返回的数组是正确排序后的,但React状态不更新。
父组件代码
import React, { useState, useEffect } from 'react'; import { dynamicSort } from '../helpers'; import Container, ChildHeader, Childbody, Spinner, etc... const Parent = () => { const [listItems, setListItems] = useState([]); useEffect(() => { // 组件挂载时获取列表数据并设置状态,这部分正常工作 }, []) const sortList = column => { setListItems(prev => dynamicSort(prev, column, 'asc')); }; return ( <Container> <ChildHeader sortList={sortList} /> <ChildBody> {listItems !== [] ? ( listItems.map(item => { return ( <ChildRow key={item.id} rowItem={item} />) }) ) : ( <Spinner /> )} </ChildBody> </Container> ); }; export default Parent;
子组件(ChildHeader)代码
const ChildHeader = ({ sortList }) => { return ( <HeaderContainer> <Column1 onClick={() => { sortList('Column1'); // 本应触发排序并更新父组件列表状态 }} > {'Column Label'} </Column1> // 更多列... </HeaderContainer> ); }; export default ChildHeader;
自定义排序函数(dynamicSort)代码
export const dynamicSort = (list, column, direction) => { const compare = (a, b) => { switch (column) { case 'Column1': if (direction === 'asc') { if (a.key1 > b.key1) { return 1; } if (a.key1 < b.key1) { return -1; } return 0; } if (direction === 'desc') { if (a.key1 < b.key1) { return 1; } if (a.key1 > b.key1) { return -1; } return 0; } // 更多列的判断逻辑... default: return null; } }; return list.sort(compare); // 已确认返回的是正确排序后的数组 }
异常现象
- 点击子组件表头能触发
sortList函数,但父组件listItems状态不更新,页面无变化 - 将
sortList改为如下代码时,状态能正常更新:
const sortList = column => { setListItems(prev => prev.slice(0, 3)); // 使用slice替代自定义排序 };
问题原因
JavaScript的Array.sort()是原地排序方法,它会直接修改原数组,返回的仍是原数组的引用。React的状态更新依赖引用变化,当你传入原状态数组prev到dynamicSort后,sort方法修改了原数组但引用没变,React对比前后状态时认为没有变化,因此不会触发组件重渲染。
而slice()方法会返回新数组的引用,React能检测到状态变化,触发更新。
修复方案
1. 修改排序函数,返回新数组
对原数组创建副本后再排序,确保返回新的数组引用:
export const dynamicSort = (list, column, direction) => { const compare = (a, b) => { switch (column) { case 'Column1': if (direction === 'asc') { if (a.key1 > b.key1) { return 1; } if (a.key1 < b.key1) { return -1; } return 0; } if (direction === 'desc') { if (a.key1 < b.key1) { return 1; } if (a.key1 > b.key1) { return -1; } return 0; } // 更多列的判断逻辑... default: return null; } }; // 创建原数组副本后排序,可选方式:[...list]、list.slice()、Array.from(list) return [...list].sort(compare); }
2. 修复空数组判断逻辑
父组件中listItems !== []永远为true(数组引用不同),应改为判断数组长度:
<ChildBody> {listItems.length > 0 ? ( listItems.map(item => { return ( <ChildRow key={item.id} rowItem={item} />) }) ) : ( <Spinner /> )} </ChildBody>
内容的提问来源于stack exchange,提问作者JBrown
相关产品推荐
相关产品推荐

