React中setState的用法

9,071 阅读4分钟

    每个人都至少有这么一个挚友,
    你和他在人生的拐点遇到,
    惊叹于彼此的不同或者相似,
    有过不少平淡无奇却值得纪念的时光,
    任白云苍狗,
    风云变幻。

                --电影《触不可及》

setState的写法可以分为两类

  1. setState(updater, [callback])

第一个参数是一个updater函数;第二个参数是个回调函数(可选)

示例如下:

this.setState((prevState, props) => {
  return {counter: prevState.counter + props.step};
});

updater这个函数格式是固定的,这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数(可选),这两个对象setState会自动传递到函数中去

修改开关状态变量经常会用updater来改变状态,例如:

this.setState(prevState => ({
  isToggleOn: !prevState.isToggleOn
}));
  1. setState(stateChange, [callback])

第一个参数是一个对象;第二个参数同上也是回调函数(可选)

示例如下:

this.setState({number: 2})

这种方式是我们最常用到的修改state的方式.

setState()将需要处理的变化塞入组件的state对象中, 并告诉该组件及其子组件需要用更新的状态来重新渲染。这是用于响应事件处理和服务端响应的更新用户界面的主要方式。

将setState()认为是一次请求而不是一次立即执行更新组件的命令。为了更为可观的性能,React可能会推迟它,稍后会一次性更新这些组件。React不会保证在setState之后,能够立刻拿到改变的结果。

setState()不是立刻更新组件。其可能是批处理或推迟更新。这使得在调用setState()后立刻读取this.state的一个潜在陷阱。代替地,使用componentDidUpdate 或一个setState回调(setState(updater, callback)),当中的每个方法都会保证在更新被应用之后触发。若你需要基于之前的状态来设置状态,阅读下面关于updater参数的介绍。


state的赋值

下面说一下对state的赋值,分为三种情况:

  1. 通常情况下的赋值

适用的类型有:数字,字符串,布尔值,null, undefined。示例如下:

this.setState({
  count: 1,
  title: 'React',
  success: true
})
  1. 数组类型的赋值

如有一个数组类型的状态books,当向books中增加一本书时,使用数组的concat方法或ES6的数组扩展语法(spread syntax):

// 方法一:将state先赋值给另外的变量,然后使用concat创建新数组
var books = this.state.books; 
this.setState({
  books: books.concat(['三体']);
})
// 方法二:使用preState、concat创建新数组
this.setState(preState => ({
  books: preState.books.concat(['三体']);
}))
// 方法三:ES6数组扩展 spread syntax
this.setState(preState => ({
  books: [...preState.books, '三体'];
}))

当从books中截取部分元素作为新状态时,使用数组的slice方法:(利用splice返回的数组也是同理)

// 方法一:将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);
}))

当从books中过滤部分元素后,作为新状态时,使用数组的filter方法:

// 方法一:将state先赋值给另外的变量,然后使用filter创建新数组
var books = this.state.books; 
this.setState({
  books: books.filter(item => {
    return item != '三体'; 
  });
})
// 方法二:使用preState、filter创建新数组
this.setState(preState => ({
  books: preState.books.filter(item => {
    return item != '三体'; 
  });
}))

注意不要使用push、pop、shift、unshift等方法修改数组类型的状态,因为这些方法都是在原数组的基础上修改,而concat、slice、splice、filter能返回一个新的数组。

  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'});
}))

使用对象扩展语法(object spread properties)

// 方法一:将state先赋值给另外的变量,然后使用对象扩展语法创建新对象
var owner = this.state.owner;
this.setState({
  owner: {...owner, name: 'Jason'};
})
// 方法二:使用preState、对象扩展语法创建新对象
this.setState(preState => ({
  owner: {...preState.owner, name: 'Jason'};
}))

this.setState更新机制图解:

每次setState产生新的state会依次被存入一个队列,然后会根据isBathingUpdates变量判断是直接更新this.state还是放进dirtyComponent里回头再说。 isBatchingUpdates默认是false,也就表示setState会同步更新this.state。 但是,当React在调用事件处理函数之前就会调用batchedUpdates,这个函数会把isBatchingUpdates修改为true,造成的后果就是由React控制的事件处理过程setState不会同步更新this.state。