本文共 3694 字,大约阅读时间需要 12 分钟。
在react开发过程中,state是组件一个重要的属性,对state的管理也尤为重要,这里记录一下踩坑经历
对于修改组件state正确的方式如下:
// 错误this.state.title = 'React';// 正确this.setState({title: 'React'});
有两个比较重要的点,一是对state的修改是异步,二是对多个相邻的state的修改可能会合并到一起一次执行。代码举例:
class Clock extends React.Component { constructor(props) { super(props); this.state = { num: 1 }; } handleClick() { this.setState((prevState, props) => ({ num: prevState.num + 1 })); console.info(this.state.num); } render() { return (); }}ReactDOM.render(Hello, world!
It is { this.state.num}.
, document.getElementById("root"));
结果在ui显示每一次加1之后的结果,而console打印的总是上一次ui的值,也就可以确定state并没有立即执行,而是异步去执行的。我们可以通过添加一个回调函数到第二个参数,让state更新完成之后来执行该回调,代码如下:
handleClick() { this.setState({ num : this.state.num + 1 }); }
此时ui上显示的数值与console打印的会是一致。另一种情况,如果对state的修改依赖state和prop,那么由于异步的原因就会产生很多异常情况,我们可以可以传入包含两个参数的函数到第一个参数来解决,代码如下:
handleClick() { this.setState((prevState, props) => ({ num: prevState.num + 1 }),() => console.info(this.state.num)); }
第一个参数prevState表示前一次的state,后一个参数表示当前状态组件的prop值,显示的让该操作同步执行
对于多次相邻的state修改操作的执行会被合并在一起执行,代码如下:
handleClick() { this.setState({ num : this.state.num + 1 },() => console.info(this.state.num)); this.setState({ num : this.state.num + 1 },() => console.info(this.state.num)); this.setState({ num : this.state.num + 1 },() => console.info(this.state.num)); }
最终数值每次只会加1,我们可以看作是以下代码的执行:
Object.assign( previousState, { num : this.state.num + 1}, { num : this.state.num + 1})
因此,被合并之后最终只会保留一个更新。解决方法和上述解决异步的方式类似:
handleClick() { this.setState((prevState,props) => ({ num : prevState.num + 1 }),() => console.info(this.state.num)); this.setState((prevState,props) => ({ num : prevState.num + 1 }),() => console.info(this.state.num)); this.setState((prevState,props) => ({ num : prevState.num + 1 }),() => console.info(this.state.num)); }
结果是我们期望的每次加3,但是需要注意的是,console打印的只会是每次加3之后的数值,并不会是每次加1的结果
React官方建议把State当作是不可变对象,一方面是如果直接修改this.state,组件并不会重新render;另一方面State中包含的所有状态都应该是不可变对象。当State中的某个状态发生变化,我们应该重新创建这个状态对象,而不是直接修改原来的状态。那么,当状态发生变化时,如何创建新的状态呢?根据状态的类型,可以分成三种情况:
状态的类型是不可变类型(数字,字符串,布尔值,null, undefined)
这种情况最简单,因为状态是不可变类型,直接给要修改的状态赋一个新值即可。如要修改count(数字类型)、title(字符串类型)、success(布尔类型)三个状态:this.setState({count: 1,title: 'Redux',success: true})
状态的类型是数组
如有一个数组类型的状态books,当向books中增加一本书时,使用数组的concat方法或ES6的数组扩展语法(spread syntax):// 方法一:将state先赋值给另外的变量,然后使用concat创建新数组var books = this.state.books; this.setState({books: books.concat(['React Guide']);})// 方法二:使用preState、concat创建新数组this.setState(preState => ({books: preState.books.concat(['React Guide']);}))// 方法三:ES6 spread syntaxthis.setState(preState => ({books: [...preState.books, 'React Guide'];}))
当从books中截取部分元素作为新状态时,使用数组的slice方法:
// 方法一:将state先赋值给另外的变量,然后使用slice创建新数组var books = this.state.books; this.setState({books: books.slice(1,3);})// 方法二:使用preState、slice创建新数组this.setState(preState => ({books: preState.books.slice(1,3);}))
注意不要使用push、pop、shift、unshift、splice等方法修改数组类型的状态,因为这些方法都是在原数组的基础上修改,而concat、slice、filter会返回一个新的数组。
状态的类型是普通对象(不包含字符串、数组)
3.1 使用ES6 的Object.assgin方法// 方法一:将state先赋值给另外的变量,然后使用Object.assign创建新对象var owner = this.state.owner;this.setState({owner: Object.assign({}, owner, {name: 'Jason'});})// 方法二:使用preState、Object.assign创建新对象this.setState(preState => ({owner: Object.assign({}, preState.owner, {name: 'Jason'});}))
3.2 使用对象扩展语法
// 方法一:将state先赋值给另外的变量,然后使用对象扩展语法创建新对象var owner = this.state.owner;this.setState({owner: {...owner, name: 'Jason'};})// 方法二:使用preState、对象扩展语法创建新对象this.setState(preState => ({owner: {...preState.owner, name: 'Jason'};}))
参考:
转载地址:http://vergi.baihongyu.com/