React生命周期方法

2019-05-14225次阅读React

了解React不同的生命周期方法对于React应用程序开发非常重要,它允许我们在需要时准确触发操作。本文将介绍React中的每个生命周期,包括它们可用的方法以及它们的使用场景。

一个react组件在应用程序中经历不同的阶段,尽管在幕后发生的事情并不明显。

  • mounting(安装)
  • updating(更新)
  • unmounting(卸载)
  • error handling(错误处理)

每个阶段都有一些方法对该阶段中的组件执行特定操作。例如,从网络获取数据时,您需要在componentDidMount()方法(该方法在安装阶段可用)中调用处理API调用的函数。

 

mounting安装阶段

把mounting安装看作组件生命周期的初始阶段。在装载发生之前,组件还没有存在——直到装载发生,并将组件作为文档的一部分挂起。

一旦React组件安装后,我们可以使用的方法:constructor(),render(),componentDidMount()和static getDerivedStateFromProps()。

constructor()构造函数

当直接在组件上设置状态以便将方法绑定在一起时,应使用constructor()方法。

// 一旦安装了输入组件…
constructor(props) {
  // …在上面设置一些props…
  super(props);
  this.state = {
    username: ''
  };
  // …然后用处理输入更改的方法绑定
  this.handleInputChange = this.handleInputChange.bind(this);
}

重要的是要知道构造函数是在创建组件时调用的第一个方法。该组件尚未呈现(即将到来),但DOM知道它,我们可以在它呈现之前钩住它。因此,这不是我们调用setState()或引入任何副作用的地方,因为,该组件仍处于构建阶段!

在使用React.createRef()时,可以在构造函数中设置ref。这是合法的,因为refs用于在不使用props的情况下更改值,或者使用更新值重新呈现组件:

constructor(props) {
  super(props);
  this.state = {
    username: ''
  };
  this.inputText = React.createRef();
}

render()

render()方法是将一个React元素渲染到根DOM节点中。

class App extends React.Component {
  // When mounting is in progress, please render the following!
  render() {
    return (
      <div>
        <p>Hello World!</p>
      </div>
    )
  }
}

还可以用于渲染组件数组:

class App extends React.Component {
  render () {
    return [
      <h2>JavaScript Tools</h2>,
      <Frontend />,
      <Backend />
    ]
  }
}

…甚至是组件的fragments碎片:

class App extends React.Component {
  render() {
    return (
      <React.Fragment>
        <p>Hello World!</p>
      </React.Fragment>
    )
  }
}

还有React.Fragment的简写语法

我们还可以使用它来呈现dom层次结构(a la React Portal)之外的组件:

// We're creating a portal that allows the component to travel around the DOM
class Portal extends React.Component {
  // First, we're creating a div element
  constructor() {
    super();
    this.el = document.createElement("div");
  }
  
  // Once it mounts, let's append the component's children
  componentDidMount = () => {
    portalRoot.appendChild(this.el);
  };
  
  // If the component is removed from the DOM, then we'll remove the children, too
  componentWillUnmount = () => {
    portalRoot.removeChild(this.el);
  };
  
  // Ah, now we can render the component and its children where we want
  render() {
    const { children } = this.props;
    return ReactDOM.createPortal(children, this.el);
  }
}

render()还可以渲染数字和字符串......

class App extends React.Component {
  render () {
    return "Hello World!"
  }
}

以及null布尔值:

class App extends React.Component {
  render () {
    return null
  }
}

componentDidMount()

在安装组件(即挂钩到DOM)之后调用此方法。此处请求从API获取数据。

fetchUsers() {
  fetch(`https://jsonplaceholder.typicode.com/users`)
    .then(response => response.json())
    .then(data =>
      this.setState({
        users: data,
        isLoading: false,
      })
    )
  .catch(error => this.setState({ error, isLoading: false }));
}

然后在componentDidMount()钩子中调用该方法:

componentDidMount() {
  this.fetchUsers();
}

我们还可以添加事件监听器:

componentDidMount() {
  el.addEventListener()
}

static getDerivedStateFromProps()

static getDerivedStateFromProps()它是在安装阶段和更新阶段之前在render()方法之前调用。它返回一个对象来更新组件的状态,或者在没有任何更新的情况下返回null值。

为了理解它是如何工作的,让我们实现一个计数器组件,它的计数器状态具有一定的值。此状态仅在maxCount的值较高时才会更新。maxCount将从父组件传递。这是父组件:

class App extends React.Component {
  constructor(props) {
    super(props)
    
    this.textInput = React.createRef();
    this.state = {
      value: 0
    }
  }
  
  handleIncrement = e => {
    e.preventDefault();
    this.setState({ value: this.state.value + 1 })
  };

  handleDecrement = e => {
    e.preventDefault();
    this.setState({ value: this.state.value - 1 })
  };

  render() {
    return (
      <React.Fragment>
        <section className="section">
          <p>Max count: { this.state.value }</p>
          <button
            onClick={this.handleIncrement}
            class="button is-grey">+</button>
          <button
            onClick={this.handleDecrement}
            class="button is-dark">-</button>          
        </section>
        <section className="section">
          <Counter maxCount={this.state.value} />
        </section>
      </React.Fragment>
    )
  }
}

我们有一个按钮用来增加maxCount的值,我们把它传递给Counter计数器组件。

class Counter extends React.Component {
  state={
    counter: 5
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    if (prevState.counter < nextProps.maxCount) {
      return {
        counter: nextProps.maxCount
      };
    } 
    return null;
  }

  render() {
    return (
      <div className="box">
        <p>Count: {this.state.counter}</p>
      </div>
    )
  }
}

在Counter组件中,我们检查counter是否小于maxCount。如果是,我们将counter设置为maxCount的值。否则,我们什么都不做。

 

updating更新阶段

更新阶段发生在组件的props或state改变时。与安装一样,更新有自己的一套可用方法,值得注意的是render()和getDerivedStateFromProps()在这个阶段都会被触发。

shouldComponentUpdate()

当组件state或props改变时,我们可以使用shouldComponentUpdate()方法来控制组件是否应该更新。此方法在渲染发生之前以及正在接收state和props时调用。默认行为是true。要在每次state或props改变时重新渲染,我们会做这样的事情:

shouldComponentUpdate(nextProps, nextState) {
  return this.state.value !== nextState.value;
}

当返回false时,组件不会更新,相反,调用render()方法来显示组件。

getSnapshotBeforeUpdate()

我们可以做的一件事是在某个时刻捕获组件的state,这就是getSnapshotBeforeUpdate()设计要做的。它在render()之后,但在将任何新更改提交到DOM之前调用。返回的值作为第三个参数传递给componentDidUpdate()。

它将先前的state和props作为参数:

getSnapshotBeforeUpdate(prevProps, prevState) {
  // ...
}

这种方法的用例还比较少。它是那些你可能不会经常接触的生命周期方法之一。

componentDidUpdate()

如果组件更新了,那么我们可以使用这个方法在那个时候钩住它,并传递它以前的props和state。

componentDidUpdate(prevProps, prevState) {
  if (prevState.counter !== this.state.counter) {
    // ...
  }
}

如果你曾经使用过getSnapshotBeforeUpdate(),你也可以将返回的值作为参数传递给componentDidUpdate():

componentDidUpdate(prevProps, prevState, snapshot) {
  if (prevState.counter !== this.state.counter) {
    // ....
  }
}

 

Unmounting卸载阶段

当组件从DOM中清除并且不再可用时,就会发生卸载。这里只有一种方法: componentWillUnmount()

这是在卸载和销毁组件之前调用的。例如删除可能已添加到componentdidmount()中的事件侦听器,或者清除订阅。

// Remove event listener 
componentWillUnmount() {
  el.removeEventListener()
}

 

Error Handling错误处理阶段

getDerivedStateFromError()
我们使用getDerivedStateFromError()捕获从子组件抛出的任何错误,然后使用这些错误更新组件的状态。

class ErrorBoundary extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      hasError: false
    };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  
  render() {
    if (this.state.hasError) {
      return (
        <h1>Oops, something went wrong :(</h1>
      );
    }

    return this.props.children;
  }
}

在本例中,当从子组件抛出错误时,ErrorBoundary组件将显示“oops,something been wrong”。更多关于此方法的信息在这里

componentDidCatch()
虽然getDerivedStateFromError()适用于在出现副作用(如错误记录)的情况下更新组件的状态,但我们应该使用componentDidcatch(),因为它是在提交阶段调用的,此时DOM已更新。

componentDidCatch(error, info) {
  // Log error to service
}
getDerivedStateFromError()和componentDidCatch()都可以在ErrorBoundary组件中使用:
class ErrorBoundary extends React.Component {
  
constructor(props) {
  super(props);
  this.state = {
    hasError: false
  };
}

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // Log error to service
  }
  
  render() {
    if (this.state.hasError) {
      return (
        <h1>Oops, something went wrong :(</h1>
      );
    }

    return this.props.children;
  }
}

 

上一篇: Webpack4内置development开发模式  下一篇: Element.closest()简化事件处理程序中匹配最近的父元素  

React生命周期方法相关文章