React之state详解

目录

执行过程

异步

React18与自动批处理

setState

推荐用法 ()=>{return },this.state. 

生命周期

数据没改变时​不渲染

shouldComponentUpdate 

PureComponent自动(推荐)

你真的理解setState吗? - 掘金

组件的私有属性,值是对象,当 state 发生变化时会触发 React 组件重新渲染视图

响应式数据

执行过程

  • _pendingStateQueue当前组件等待执行更新的state队列
  • isBatchingUpdates:react用于标识当前是否处于批量更新状态,所有组件公用
  • dirtyComponent:当前所有处于待更新状态的组件队列

事务机制:数据库特有的术语,同步发生数据更新时,防止数据的不一致。

1.将setState传入的partialState参数存储在当前组件实例state暂存队列中。

2.判断当前React是否处于批量更新状态,如果是,将当前组件加入待更新的组件队列中。

3.如果未处于批量更新状态,将批量更新状态标识设置为true,用事务再次调用前一步方法,保证当前组件加入到了待更新组件队列中。

4.调用事务的waper方法,遍历待更新组件队列依次执行更新。

5.执行生命周期componentWillReceiveProps

6.将组件的state暂存队列中的state进行合并,获得最终要更新的state对象,并将队列置为空。

7.执行生命周期componentShouldUpdate,根据返回值判断是否要继续更新。

8.执行生命周期componentWillUpdate

9.执行真正的更新render

10.执行生命周期componentDidUpdate

异步

setState其实本身执行的过程和代码都是同步的

自动批处理:同一时机多次调用`setState()`方法的一种处理机制,有助于减少在状态更改时发生的重新渲染次数。

handleClick = () => {  
    this.setState({
        msg: 'hi'
    });
    this.setState({
        count: 1
    });
}

//虽然调用了两次`setState()`方法,但是只会触发一次`render()`方法的重新执行。

React18与自动批处理

React18之前也有批处理的,但是在Promise、setTimeout、原生事件中是不起作用的,只在合成事件和钩子函数有用

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

react为了解决跨平台,兼容性问题,自己封装了一套事件机制,代理了原生的事件。

像在jsx中常见的onClickonChange这些都是合成事件

handleClick = () => {  
    setTimeout(()=>{
        this.setState({
            msg: 'hi'
        });
        this.setState({
            count: 1
        });
    }, 2000)
}

//上面代码在React18之前的版本中,将会触发两次`render()`方法。
//默认是自动批处理的,当然也可以改成不是自动批处理的方式,通过`ReactDOM.flushSync`这个方法。
import { flushSync } from 'react-dom'; // Note: react-dom, not react
handleClick = () => {  
    ReactDOM.flushSync(()=>{
        this.setState({
            msg: 'hi'
        });
    })
    ReactDOM.flushSync(()=>{
        this.setState({
            count: 1
        });
    }) 
}

 useState(initialState: S | (() => S));

let { useState } = React;
let { flushSync } = ReactDOM;
let Welcome = (props) => {
    const [count, setCount] = useState(0);
    const handleClick = () => {
        flushSync(()=>{
          setCount(count + 1)
        })
    }
    return (
        <div>
            <button onClick={handleClick}>点击</button>
            <div>hello world, { count }, { msg }</div>
        </div>
    );
}

当所有组件didmount后,父组件didmount,会将isBranchUpdate设置为false。这时将执行之前累积的setState,更新时会把每个组件的更新合并,每个组件只会触发一次更新的生命周期。

setState

setState(state: (prevState, props) => (return newState),callback?: () => void): void;

状态更新完成后会调用该回调函数callback

props 同步更新,state 异步更新的,最好不要用prevState值去计算下一个 state 的值

this.setState((prevState, props) => {  return { count: prevState.count + 1 }})

state:如果是一个对象,那么会将该对象与原状态进行浅合并

this.setState({
    
    count:1}) 
//箭头函数:相当于直接使用返回对象 {count: 1}
this.setState(() => ({
    
    count: 1}));
//同样的数据修改只会修改一次,可利用`setState()`的回调函数写法来保证每一次都能触发。
//因为不能比较函数体是否一致
this.setState((state)=> ({
    
    count: state.count + 1}));
this.setState((state)=> ({
    
    count: state.count + 1}));

合并(shallow merge)是指将两个对象进行合并,如果两个对象有相同的属性名,则后一个对象的属性值会覆盖前一个对象的属性值

但是,如果属性值是一个对象,则不会进行递归合并,而是直接用后一个对象的属性值替换前一个对象的属性值。

useState不会合并之前的值

const [info, setInfo] = useState({
    username: 'xiaoming',
    age: 20
})
setInfo({
    ...info,
    username: 'xiaoqiang'
})

推荐用法 ()=>{return },this.state. 

setState时使用函数返回新state值,(避免多个 setState 同时调用导致的状态更新冲突问题)

回调函数中获取最新更新后的state

handleClick = () => {
    this.setState(() => { return { board: this.jsonMsg[0].data.board_data[Number(key)] };}, 
() => {
      console.log('Updated count:', this.state.count);
    });
  };

生命周期

生命周期钩子函数就是回调函数

挂载

  • constructor():在 React 组件挂载之前,会调用它的构造函数。(注:如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。)
  • render(): class 组件中唯一必须实现的方法。
  • componentDidMount():在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。

更新

  • render(): class 组件中唯一必须实现的方法。一旦组件被添加到 DOM,它只有在 prop 或状态发生变化时才可能更新和重新渲染
  • componentDidUpdate():在更新后会被立即调用。首次渲染不会执行此方法。

卸载

  • componentWillUnmount():在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。
     

可以看到挂载时和更新时都有render这个方法。这就是为什么state改变的时候,会触发render重渲染操作

数据没改变时​不渲染

shouldComponentUpdate 

在调用`setState()`方法的时候,如果数据没有改变,实际上也会重新触发`render()`方法。

shouldComponentUpdate = (nextProps, nextState) => {
        if(this.state.msg === nextState.msg){
            return false;
        }
        else{
            return true;//重新渲染
        }
    }

PureComponent自动(推荐)

自动完成选择性的渲染

export default class App extends PureComponent<any, any, any>{
 handleClick = () => {  
        this.setState({
          list: [...this.state.list, 'd']
        }); 
        //错误✖
        /* this.state.list.push('d');
        this.setState({
          list: this.state.list
        }) */
    }
} 

猜你喜欢

转载自blog.csdn.net/qq_28838891/article/details/131277884