react 之 setState

class Example extends React.Component {
  constructor() {
    super();
    this.state = {
      val: 0
    };
  }
  
  componentDidMount() {
    this.setState({val: this.state.val + 1});
    console.log(this.state.val);    // 第 1 次 log  0

    this.setState({val: this.state.val + 1});
    console.log(this.state.val);    // 第 2 次 log   0

    setTimeout(() => {
      this.setState({val: this.state.val + 1});
      console.log(this.state.val);  // 第 3 次 log  2

      this.setState({val: this.state.val + 1});
      console.log(this.state.val);  // 第 4 次 log  3
    }, 0);
  }
  render() {
    return null;
  }
};
// 假如
 componentDidMount(){
  for ( let i = 0; i < 100; i++ ) {
    this.setState( { num: this.state.num + 1 } );
  }
}

组件渲染的结果是1,·且在控制台中输出了100次0,说明每个循环中,拿到的state仍然是更新之前的。

react 如何优化以上?

setState接收的参数还可以是 一个函数,在这个函数中可以拿先前的状态,并通过这个函数的返回值得到下一个状态

componentDidMount() {
    for ( let i = 0; i < 100; i++ ) {
        this.setState( prevState => {
            console.log( prevState.num );
            return {
                num: prevState.num + 1
            }
        } );
    }
}

合成事件中的setState

合成事件,react为了解决跨平台,兼容性问题,自己封装了一套事件机制,代理了原生的事件,像在jsx中常见的onClick、onChange这些都是合成事件
合成事件中也有batchedUpdates方法,是通过同样的事务完成的

class App extends Component {

  state = { val: 0 }

  increment = () => {
    this.setState({ val: this.state.val + 1 })
    console.log(this.state.val) // 输出的是更新前的val --> 0
  }

  render() {
    return (
      
{`Counter is: ${this.state.val}`}
) } }

生命周期函数中的setState

整个生命周期中就是一个事物操作,所以标识位isBatchingUpdates = true,所以流程到了enqueueUpdate()时,实例对象都会加入到dirtyComponents 数组中

class App extends Component {

  state = { val: 0 }

 componentDidMount() {
    this.setState({ val: this.state.val + 1 })
   console.log(this.state.val) // 输出的还是更新前的值 --> 0
 }
  render() {
    return (
      
{`Counter is: ${this.state.val}`}
) } }

其实还是和合成事件一样,componentDidmount 执行的时候,react内部并没有更新,执行完componentDidmount 后才去 commitUpdateQueue 更新。这就导致你在 componentDidmount 中 setState 完去console.log拿的结果还是更新前的值

原生事件中的setState

原生事件是指非react合成事件,原生自带的事件监听 addEventListener ,或者也可以用原生js、jq直接 document.querySelector().onclick这种绑定事件的形式都属于原生事件

原生事件绑定不会通过合成事件的方式处理,自然也不会进入更新事务的处理流程。setTimeout也一样,在setTimeout回调执行时已经完成了原更新组件流程,不会放入dirtyComponent进行异步更新,其结果自然是同步的

class App extends Component {

  state = { val: 0 }

  changeValue = () => {
    this.setState({ val: this.state.val + 1 })
    console.log(this.state.val) // 输出的是更新后的值 --> 1
  }

 componentDidMount() {
    document.body.addEventListener('click', this.changeValue, false)
 }
 
  render() {
    return (
      
{`Counter is: ${this.state.val}`}
) } }

批量更新

在 setState 的时候react内部会创建一个 updateQueue ,通过 firstUpdate 、 lastUpdate 、 lastUpdate.next去维护一个更新的队列,在最终的 performWork 中,相同的key会被覆盖,只会对最后一次的 setState 进行更新

class App extends Component {

  state = { val: 0 }

  batchUpdates = () => {
    this.setState({ val: this.state.val + 1 })
    this.setState({ val: this.state.val + 1 })
    this.setState({ val: this.state.val + 1 })
 }

  render() {
    return (
      
{`Counter is ${this.state.val}`} // 1
) } }
  • 在官方的描述中,setState操作并不保证是同步的,也可以认为是异步的。

  • React在setState之后,会经对state进行diff,判断是否有改变,然后去diff dom决定是否要更新UI。如果这一系列过程立刻发生在每一个setState之后,就可能会有性能问题。

  • 在短时间内频繁setState。React会将state的改变压入栈中,在合适的时机,批量更新state和视图,达到提高性能的效果

生命周期函数里面可以setState 吗? 什么时候setState 合适?

  • 在 componentWillMount中执行 setState 是无意义的,应该将这里的 setState 放到初始化 this.state 的地方去(如 constructor)直接作为 state 的初始值。

原因是:组件只挂在一次,在componentWillMount里setState 会但是仅会更新state一次,而且会和 constructor 里的初始化 state 合并执行,因此这是无意义的 setState。

  • 禁止在 shouldComponentUpdate 和 componentWillUpdate中调用setState,这会造成循环调用,直至耗光浏览器内存后崩溃

shouldComponentUpdate 或者 componentWillUpdate 里调用 setState 会再次触发这两个函数,然后在两个函数又触发了 setState,然后再次触发这两个函数

  • 在 componentDidUpdate 中执行 setState 会导致组件刚刚完成更新,又要再更新一次,连续渲染两遍(和在 componentDidMount 中执行 setState 类似)

  • componentWillReceiveProps中可以 setState,不会造成二次渲染。
    由于只有 props 的变化才会触发 componentWillReceiveProps 事件,因为在这个事件里 setState 不会造成不停触发组件更新的死循环,可以放心在这个函数里 setState

https://github.com/hujiulong/blog/issues/7

https://juejin.im/post/5b45c57c51882519790c7441

你可能感兴趣的:(react 之 setState)