React中用setState排序数组后,如何切回排序前的原始预览状态?
现有代码的核心问题梳理
在给出实现方案前,先把你当前代码里会导致逻辑失效的硬伤列清楚:
- 违反React单向数据流规则:
Array.sort()是原地修改原数组的方法,你直接调用this.props.movies.sort(),相当于直接篡改了父组件传下来的props数据,props本身是只读的,这种写法会导致数据源混乱,是React开发的常见踩坑点。 - 状态使用逻辑错误:
setState的入参应该是「要更新的状态键值对对象」,你现在直接把sort后的数组作为setState的参数传入,相当于把组件的整个state替换成了一个数组,完全不符合React状态更新的规范。 - 缺少切换逻辑必需的两个核心信息:
- 没有留存从未被修改过的原始未排序电影数组,哪怕你想切回初始顺序,也找不到原来的数据了
- 没有记录当前页面展示的内容是「排序后状态」还是「原始未排序状态」,根本没法判断点击按钮后该执行排序还是恢复顺序的操作。
具体实现步骤
1. 初始化组件状态
在类组件的构造函数里初始化三个核心状态字段:
originalMovies:专门存从接口拿到的、从未做过任何修改的原始电影数组,作为恢复顺序的数据源isSorted:布尔值标记,默认false,代表当前展示的是原始顺序的内容displayMovies:当前要在页面上展示的电影列表,默认是空数组,等接口数据回来之后填充
2. 同步接口数据到组件状态
因为电影数据是异步接口获取的,等数据更新到props后,要把原始数据拷贝一份存到组件state里,避免后续操作改动原数据源。
3. 改造点击按钮的切换逻辑
点击按钮时做分支判断:
- 如果当前是未排序状态:基于原始数组拷贝一份新数组,执行按title排序的逻辑,把排序后的数组赋值给
displayMovies展示,同时把isSorted标记设为true - 如果当前已经是排序状态:直接把
displayMovies切回之前存的originalMovies原始数组,把isSorted标记设回false
完整的改造后代码
export class MoviesIndexContainer extends Component { // 初始化组件状态 constructor(props) { super(props); this.state = { originalMovies: [], // 永久保存的原始未排序数据 isSorted: false, // 标记当前是否处于排序状态 displayMovies: [] // 页面实际渲染的电影列表 } } handleClick = () => { // 分支判断当前是要排序还是要恢复 if (!this.state.isSorted) { // 基于原始数组拷贝新数组做排序,避免改动原始数据 const sortedMovies = [...this.state.originalMovies].sort((a, b) => { const nameA = a.title.toUpperCase(); const nameB = b.title.toUpperCase(); if (nameA < nameB) return -1; if (nameA > nameB) return 1; return 0; }) // 更新状态:切到排序后的列表,标记为已排序 this.setState({ displayMovies: sortedMovies, isSorted: true }) } else { // 切回原始未排序列表,重置排序标记 this.setState({ displayMovies: this.state.originalMovies, isSorted: false }) } }; componentDidMount() { this.props.DispatchfetchMovies(); } // 接口数据回来后同步到组件state componentDidUpdate(prevProps) { // 只有当props的movies完成加载、拿到实际数据时,才同步存到state里 if (prevProps.movies !== this.props.movies && this.props.movies.length > 0) { this.setState({ originalMovies: [...this.props.movies], // 浅拷贝存原始数据 displayMovies: [...this.props.movies] // 初始展示就是原始顺序 }) } } render(){ if (this.props.loadingState === 'notStarted'){ return null } return ( <> {/* 可以根据排序状态动态改按钮文案,提升交互体验 */} <button onClick={this.handleClick} className="inline-block border border-blue-500 rounded py-2 px-3 mt-6 bg-blue-500 text-white mx-0.5" > {this.state.isSorted ? '恢复原始顺序' : '按名称排序'} </button> <section className="max-w-6xl w-11/12 mx-auto mt-16"> { this.props.loadingState === 'inProgress' ? ( 'loading content') : ( <MovieList movies={this.state.displayMovies}/> )} </section> </> ) } }
开发注意细节
- 不管是排序还是恢复数据,只要是操作数组,都要先拷贝出一个新数组再调用
sort、splice这类原地修改的方法,绝对不要直接操作props传入的数组。 - 不要依赖
this.props.movies作为恢复顺序的数据源,如果之前不小心直接sort了props的数组,props本身的顺序就已经被改乱了,根本找不回初始的排列顺序。 - 之前的写法里
setState直接接收数组作为参数是完全错误的,React的setState永远接收键值对格式的对象,用来对应你要修改的state字段。
内容的提问来源于stack exchange,提问作者Jose Reyes
相关产品推荐
相关产品推荐

