赞
踩
要点:
使用:
import React, { Component } from 'react'; class App extends Component { // 初始化方式1 // state = { // num: 100 // } // 初始化方式2 constructor(props) { super(props); this.state = { num: 100 } } addNum(evt, n = 1) { // 同步修改数据,它不会触发视图更新 this.state.num += n this.forceUpdate() console.log(this.state.num); } render() { return ( <div> {/* 在视图中读取state中的数据 */} <h3>{this.state.num}</h3> <button onClick={evt => this.addNum(evt, 2)}>累加</button> </div> ); } } export default App;
概述:
修改 state 中的 num 属性数据的同时,让视图更新,用到专门用来修改 state 中数据的方法 setState。
语法:
# 语法1
this.setState({
key:value
})
# 语法2 推荐
this.setState(state => {
return {key:value}
})
使用:
import React, { Component } from 'react'; class App extends Component { state = { num: 100 } addNum(evt, n = 1) { // 对象方式更新 批处理 => 数组【队列】 虚拟dom比对 // 它是一个异步操作 this.setState({ num: this.state.num + 1 }) console.log(this.state.num); // this.setState({ // num: this.state.num + 1 // }, () => { // // 回调函数中就可以得到最新的状态数据 // console.log(this.state.num); // }) } render() { return ( <div> {/* 在视图中读取state中的数据 */} <h3>{this.state.num}</h3> <button onClick={evt => this.addNum(evt, 2)}>累加</button> </div> ); } } export default App;
注意:
注意执行结果中,虽然视图发生了更新,但是控制台打印 state 中的数据并没有发生改变,说明这里的更新数据是异步操作。
之所以是异步操作,是因为 React 在这里做了批处理。即当执行多次修改数据的操作时,React 并不会立即执行,而是将这些操作存入一个异步队列中,然后再进行统一处理,这样做可以提升性能。假如我现在要修改 3 次数据,不用批处理需要更新视图 3 次,而如果进行批处理的话,只需要更新视图一次。更新视图需要进行 dom 比对,只比 1 次显然比比对 3 次性能更好。
关于 setState 方法(React 批处理)中的对象合并:
import React, { Component } from 'react'; class App extends Component { state = { num: 100 } addNum(evt, n = 1) { this.setState({ num: this.state.num + 1 }) this.setState({ num: this.state.num + 2 }) this.setState({ num: this.state.num + 3 }) console.log(this.state.num); // 函数,它不会合并,它会依次执行 == 建议多用函数方式,保证数据完整性 // this.setState(state => ({ // num: state.num + 1 // })) // this.setState(state => ({ // num: state.num + 2 // })) // this.setState(state => ({ // num: state.num + 3 // })) // console.log(this.state.num);//6 } render() { return ( <div> {/* 在视图中读取state中的数据 */} <h3>{this.state.num}</h3> <button onClick={evt => this.addNum(evt, 2)}>累加</button> </div> ); } } export default App;
从图中运行结果可以看出,当我们多次修改数据时,视图只针对最后一次修改作出渲染。这是因为在 React 批处理中(批处理队列是一个集合,这里看作是一个对象),会进行对象合并。什么意思呢?首先对象的 key 值是唯一的,当 key 值 num 已经存在时,再传 num 会对对象中已经存在的 num 进行修改,即对象中有效的 num 的值是最后一次操作:this.state.num + 3
。
补充说明:批处理队列的底层进行的是
[...obj1,...obj2]
的操作,所以重复的项只会出现一次。而函数方式的批处理进行的是[fn1,fn2...]
,所以会依次执行。这里的解释过于牵强和抽象,只是为了简单理解和记忆,更加完整的阐述需要了解底层之后,再加深理解。
关于 setState 方法的同步操作:
react17 及之前版本,如果你把当前的操作不写在合成事件中,则 setState 它就是同步的,react18全是异步的。
setState 在执行的过程中,它会判断当的执行环境,如果为宏任务等,则它会立即执行。
也就是说, setState 方法只要写在合成事件处理方法中,它就是异步的;只要不写在合成事件中,它都是同步。(仅限于 react17 及之前版本)
比如 setState 方法写在宏任务中或者写在原生事件中就是同步的。
例如下面这样的写法就是同步操作:
import React, { Component } from 'react'; class App extends Component { // setState它是同步的也是异步的 // 只要写在合成事件处理方法中,它就是异步 // 只要不写在合成事件中,它都是同步 state = { num: 100 } addNum(evt, n = 1) { setTimeout(() => { // 写在宏任务中的setState它是同步的 this.setState({ num: this.state.num + 3 }) console.log(this.state.num); }, 0); } // 类似于vue中的mounted方法 // componentDidMount() { // // 写在原生事件中它的setState也是同步的 // // document.onclick = () => { // // this.setState({ // // num: this.state.num + 3 // // }) // // console.log(this.state.num); // // } // } render() { return ( <div> {/* 在视图中读取state中的数据 */} <h3>{this.state.num}</h3> <button onClick={evt => this.addNum(evt, 2)}>累加</button> </div> ); } } export default App;
import React, { Component } from 'react'; class App extends Component { state = { todos: [] } onEnter = evt => { if (evt.keyCode === 13) { let title = evt.target.value.trim() // 方案1 // let todos = this.state.todos.concat({ id: Date.now(), title, done: false }) // this.setState({ todos }) // 方案2 // this.state.todos.push({ id: Date.now(), title, done: false }) // this.forceUpdate() // 这里也可以写成这一句:this.setState({}) // 如果你setState写了一个空对象,则它会更新视图一次 // this.setState({}) // 如果你写了为null,setState不会做任何事件,相当于没有调用 // this.setState(null) // 方案3:更新数据,推荐,确保数据的完整性 this.setState(state => ({ todos: [...state.todos, { id: Date.now(), title, done: false }] }), () => evt.target.value = '') } } del = tid => () => { this.setState(state => ({ todos: state.todos.filter(({ id }) => id != tid) })) } delIndex = index => () => { this.state.todos.splice(index, 1) this.setState({}) } render() { return ( <div> <div> <input type="text" onKeyUp={this.onEnter} /> </div> <hr /> <ul> { this.state.todos.map((item, index) => ( <li key={item.id}> <span>{item.title}</span> {/* 按 id 删除 */} <span onClick={this.del(item.id)}>删除</span> {/* 按索引删除 */} <span onClick={this.delIndex(index)}>删除</span> </li> )) } </ul> </div> ); } } export default App;
注意:
setState 还有两种扩展用法:
如果你setState写了一个空对象,则它会更新视图一次
this.setState({})
如果你写了为null,setState不会做任何事件,相当于没有调用
this.setState(null)
import React, { Component } from 'react'; class App extends Component { state = { carts: [ { id: 1, name: '水果手机14', price: 1, num: 1 }, { id: 2, name: '大米手机14', price: 1, num: 2 }, { id: 3, name: '一般手机14', price: 1, num: 3 }, ] } setNum = (n, index) => { // 方案1 this.state.carts[index]['num'] += n if (this.state.carts[index]['num'] <= 1) this.state.carts[index]['num'] = 1 if (this.state.carts[index]['num'] >= 5) this.state.carts[index]['num'] = 5 this.setState({}) // 方案2 // this.setState(state => { // state.carts[index]['num'] += n // let carts = state.carts // return { carts }// 同等于 return {carts:carts} // }) } totalPrice = () => { return this.state.carts.reduce((p, c) => { p += c.num * c.price return p }, 0) } render() { return ( <div> <table width='600' border='1'> <thead> <tr> <th>ID</th> <th>名称</th> <th>价格</th> <th>数量</th> </tr> </thead> <tbody> { this.state.carts.map((item, index) => ( <tr key={item.id}> <td>{item.id}</td> <td>{item.name}</td> <td>{item.price}</td> <td> <button onClick={() => this.setNum(-1, index)}>---</button> <span>{item.num}</span> <button onClick={() => this.setNum(1, index)}>+++</button> </td> </tr> )) } </tbody> </table> <hr /> <h3>{this.totalPrice()}</h3> </div> ); } } export default App;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。