react life cycle&State

Lifecycle&State


Click to jump to source:

Lifecycle methods

We need to trigger different event logic at different stages of react component rendering, so we need life cycle methods. Here are some commonly used life cycle functions: : The component has
componentDidMount()been rendered.
componentDidUpdate(): The component will be called immediately after it is updated.
componentWillUnmount(): The component is about to be destroyed.
Uncommonly used life cycle functions:
static getDerivedStateFromProps(props, state): Called before the render method, and will be called during initial mount and subsequent updates.  Documentation:
shouldComponentUpdate() : Based on the return value, determine whether the output of the React component is affected by changes in the current state or props .
getSnapshotBeforeUpdate(prevProps, prevState): Called before the latest rendering output (submitted to the DOM node).

class HelloWorld extends React.Component {
      componentDidMount(){
        console.log('组件渲染完毕...');
      }
      componentWillUnMount(){
        console.log('组件即将销毁...');
      }
      render(){
        console.log('render...')
        return (
          <div className={'danger large'}>
            <h2>倒计时</h2>
            { /* className和class属性是一样的,而且必须是驼峰 */ }
            <span>{ new Date().toString() }</span>
          </div>
        )
      }
    }
    // 通过调用React自身方法render可以得到当前组件的实例对象,并渲染到页面容器.
    ReactDOM.render(<HelloWorld />,document.getElementById('root'));

Declare the current time in  componentDidMount:

class HelloWorld extends React.Component {
  componentDidMount(){
    console.log('this',this);
    this.nowTime = new Date().getTime();
  }
  componentWillUnMount(){
    console.log('组件即将销毁...');
  }
  render(){
    console.log('render...')
    return (
      <div className={'danger large'}>
        <h2>倒计时</h2>
        { /* className和class属性是一样的,而且必须是驼峰 */ }
        <span>{ this.nowTime }</span>
      </div>
    )
  }
}
// 通过调用React自身方法render可以得到当前组件的实例对象,并渲染到页面容器.
ReactDOM.render(<HelloWorld />,document.getElementById('root'));

We found that the instance object can be printed out and the nowTime attribute exists, but the page is not rendered. ** Why? **
Because renderit is only executed once during initialization, nowTimebut componentDidMountadded in the life cycle function.
In other words, if We want to get the result of page rendering:

  1. Create first in constructornowTime
  2. The value componentDidMount modified innowTime

Constructor

  1. Create first in constructornowTime

We can add a constructor to the class  constructor() . If you are not clear about the es6 class part, you need to review the relevant knowledge points .
We found that  * this*constructor() in  and   this are consistent and point to  the instance object of the component. Note: Constructor super() in must be called,  documentation  .render()HelloWorld

  1. The value componentDidMount modified innowTime

We tried to modify nowTime in the life cycle function, and found that the modified content was also printed, but the page was not rendered. In other words, directly manually modifying the  properties of the instance object will not trigger**render() ** * execution* (this is bidirectional with vue Data binding is different (react does not have two-way data binding).
So how can it be retriggered when the data is modified render()?

class HelloWorld extends React.Component {
      constructor(){
        // 如果写了构造函数,那么super必须写
        super();
        console.log('构造函数...');
        this.nowTime = new Date().getTime();
      }
      componentDidMount(){
        console.log('this',this);
        setTimeout(()=>{
          this.nowTime = '新时间';
          console.log(new Date().getTime(),this.nowTime)
        },3000)
      }
      componentWillUnMount(){
        console.log('组件即将销毁...');
      }
      render(){
        console.log('render...')
        return (
          <div className={'danger large'}>
            <h2>倒计时</h2>
            { /* className和class属性是一样的,而且必须是驼峰 */ }
            <span>{ this.nowTime }</span>
          </div>
        )
      }
    }
    // 通过调用React自身方法render可以得到当前组件的实例对象,并渲染到页面容器.
    ReactDOM.render(<HelloWorld />,document.getElementById('root'));

state and setState()

Create responsive properties

State  is a property of a component, and the state in a component contains data that may change at any time. state is defined by the user and is an ordinary JavaScript object.
We cannot directly modify the state itself. We can  **setState()**modify the state through the api ** ** method.
Use the value componentDidMount modified in setState nowTime:

class HelloWorld extends React.Component {
      constructor(){
        // 如果写了构造函数,那么super必须写
        super();
        console.log('构造函数...');
        this.state = {
          nowTime: new Date().getTime()
        }
      }
      componentDidMount(){
        console.log('this',this);
        setTimeout(()=>{
          this.setState({
            nowTime: '新时间'
          })
          console.log(new Date().getTime(),this.state.nowTime)
        },3000)
      }
      componentWillUnMount(){
        console.log('组件即将销毁...');
      }
      render(){
        console.log('render...')
        return (
          <div className={'danger large'}>
            <h2>倒计时</h2>
            { /* className和class属性是一样的,而且必须是驼峰 */ }
            <span>{ this.state.nowTime }</span>
          </div>
        )
      }
    }
    // 通过调用React自身方法render可以得到当前组件的实例对象,并渲染到页面容器.
    ReactDOM.render(<HelloWorld />,document.getElementById('root'));

The page is updated, that is to say, a responsivestate object can be created   , through which we can modify the data and trigger  a function to update the page.setState()render()

Note 1: Do not modify State directly

// 这样无法触发UI更新
this.state.nowTime = 'xxx';
//而应该使用
this.setState({nowTime='xx'});

Note 2: State updates may be asynchronous

For performance reasons, React may  [setState()](https://zh-hans.reactjs.org/docs/react-component.html#setstate) combine multiple calls into a single call. So you don't rely on their values ​​to update the next state.

// 这种事错误的
this.setState({
  counter: this.state.counter + 1,
});
// 可以通过回调方法来获取到上一次更新后的state.
// 回调方法接收两个参数,props下一章节再讨论
this.setState((state,props) => ({
  counter: state.counter + 1
}));

callback:

setState(updater, [callback])

Calling setState() in setTimeout

componentDidMount(){
  console.log('组件挂载完毕...');
  // 1. 更新一次count ,检查setState()是否是异步的
  // 2. setState()会合并更新,某一次更新如果依赖于上一次的值,可能会有问题
  // 3. setState() 如果写到setTimeout/setInterval中,会变成同步函数.
  // 尝试修改nowTime
  setTimeout(()=>{
    this.setState({
      count: this.state.count + 1   // 1
    })
    console.log('this.state.count_1',this.state.count);
    this.setState({
      count: this.state.count + 1  // 2
    })
    // 不能直接修改state的值,需要通过setState()
    this.setState({
      nowTime: new Date().toString()
    })
    // console.log('this.nowTime',this.state.nowTime);
    console.log('this.state.count_2',this.state.count); // 2?
  },3000)
}

Note 3: State updates will be merged

setState is a shallow merge of state, which only modifies the same properties and retains other properties.

constructor(props) {
    super(props);
    this.state = {
      posts: [],
      comments: []
    };
  }
componentDidMount() {
  fetchPosts().then(response => {
    this.setState({
      posts: response.posts
    });
  });

  fetchComments().then(response => {
    this.setState({
      comments: response.comments
    });
  });
}

Complete lifecycle function

Disclaimer: It is more appropriate to explain it after the Ref course.

constructor(): Constructor.Render
render(): /Update.
componentDidMount(): The component has been rendered.
componentDidUpdate(): Will be called immediately after the component is updated. :
componentWillUnmount()The component is about to be destroyed.
Uncommonly used life cycle functions:
static getDerivedStateFromProps(props, state): is called before the render method, and will be called during the initial mount and subsequent updates. Call.Documentation  :
shouldComponentUpdate() : Based on the return value, determine whether the output of the React component is affected by the current state or props changes.
getSnapshotBeforeUpdate(prevProps, prevState): Called before the latest rendered output (submitted to the DOM node).

class HelloWorld extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      number: 1,
    };
    this.ulRef = React.createRef();
    console.log('constructor...');
  }
  static getDerivedStateFromProps(props, state) {
    console.log("getDerivedStateFromProps...", this);
    return {
      age: 10,
    };
  }
  componentDidMount() {
    console.log("componentDidMount...");
  }
  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log("componentDidUpdate...", snapshot);
  }
  shouldComponentUpdate(){
    console.log('shouldComponentUpdate...',this.state.number)
    // 判断是否渲染UI
    // return (this.state.number%2==0)
    return true;
  }
  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log("getSnapshotBeforeUpdate...");
    return null;
  }
  incNumber() {
    this.setState((state) => ({
      number: ++state.number,
      age: ++state.age,
    }));
  }
  render() {
    console.log("render...", this);
    return (
      <div>
        <ul ref={this.ulRef} onClick={() => this.incNumber()}>
          {Array(this.state.number)
            .fill(null)
            .map((item, index) => (
            <li key={index}>{index}__</li>
          ))}
        </ul>
        <p>age: {this.state.age}</p>
      </div>
    );
  }
}
ReactDOM.render(<HelloWorld />, document.getElementById("root"));

Understand:getSnapshotBeforeUpdate(prevProps, prevState)

getSnapshotBeforeUpdate() is called before the latest rendering output (submitted to the DOM node). It enables the component to capture some information (for example, scroll position) from the DOM before it changes. Any return value from this lifecycle method will be passed as a parameter to componentDidUpdate().
This usage is uncommon, but it may occur in UI processing, such as chat threads that need to handle scroll position in a special way.
The value of snapshot (or null) should be returned.

<style>
    *{
      margin: 0;
      padding: 0;
    }
    .ul-chat{
      width: 200px;
      height: 200px;
      border: 1px solid red;
      margin: 30px;
      overflow: auto;
    }
    .ul-chat>li{
      height: 30px;
      font-size: 20px;
      line-height: 30px;
      text-indent: 2em;
    }

  </style>

class HelloWorld extends React.Component {
      constructor(props){
        super(props);
        console.log('constructor....');
        // 创建ref
        this.chatUlRef = React.createRef();
        this.state = {
          chatItems: [
            {
              id: 1,
              text: '吃了么'
            },
            {
              id: 2,
              text: '吃了!'
            },
            {
              id: 3,
              text: '吃啥了?'
            },
            {
              id: 4,
              text: '吃好吃的!'
            }
          ]
        }
        // bind
        this.handleKeyUp = this.handleKeyUp.bind(this);
      }

      componentDidMount(){

      }
      // 在state被更新后触发,而又没来得及更新到UI的时候
      // 这里可以获取到更新之前的UI状态,比如滚轮位置
      // prevState 上一次的state
      // 此生命周期方法的任何返回值将作为参数传递给 componentDidUpdate()。
      getSnapshotBeforeUpdate(prevProps, prevState){
        // 1. 得到之前容器的高度
        // 注意: 必须是等内容高度发生变化的时候再处理
        if(this.state.chatItems.length > prevState.chatItems.length){
          // 这里没来得及更新UI,所以得到的是上一次更新后的DOM
          var chatUl = this.chatUlRef.current;
          // 之前的容器内容高度
          var preContentHeight = chatUl.scrollHeight - chatUl.scrollTop;
          return preContentHeight;
        }
        // 如果聊天列表高度没变化,则返回null
        return null;

      }
      // snapshot 是通过getSnapshotBeforeUpdate声明周期函数返回的
      componentDidUpdate(prevProps, prevState, snapshot){
        console.log('didUpdate....',this.state);
        console.log('snapshot',snapshot);
        // 2. 用更新后的最新高度 - 原来的内容高度 得到的就是需要被卷进去的高度
        if(snapshot){
          var chatUl = this.chatUlRef.current;
          // 计算需要被卷入的高度(多余的高度)
          var useScrollTop = chatUl.scrollHeight - snapshot;
          // 3. 赋值
          chatUl.scrollTop = useScrollTop;
        }

      }
      // 如果是输入了enter,需要把最新文本内容 更新到state.chatItems
      handleKeyUp(e){
        // keyCode 可以获取键盘按键的 Ascii编码值
        var keyCode = e.keyCode;
        // 如果值== 13 说明是enter键
        if(keyCode == 13){
          // 获取input的value
          // 不能直接修改state的值,必须通过setState才能触发render()!!!
          // 所以如果state的某个值是对象类型,需要先克隆
          // [...xxx] 数组结构
          var chatItems = [...this.state.chatItems];
          chatItems.push({
            id: new Date().getTime(), // 时间戳肯定不同
            text: e.target.value
          })
          // 用setState更新
          this.setState({
            chatItems
          })
          // 更新后还原输入框
          e.target.value = '';
        }
      }
      render(){
        console.log('render....',this.state);
        var liItems = this.state.chatItems.map(item=>(
          <li key={item.id}>{item.text}</li>
        ))
        return (
          <div>
              <input type="text" onKeyUp={this.handleKeyUp} placeholder="请输入聊天内容" />
              <ul className="ul-chat" ref={this.chatUlRef}>
                  {liItems}
              </ul>
          </div>
        )
      }
    }
    // 使用react 相关api 渲染到容器
    ReactDOM.render(<HelloWorld />,document.getElementById('root'));

Guess you like

Origin blog.csdn.net/m0_67388537/article/details/131937382