【转】React 16 中从 setState 返回 null 的妙用

概述

在 React 16 中为了防止没必要要的 DOM 更新,容许你决定是否让 .setState 更来新状态。在调用 .setState 时返回 null 将再也不触发更新。css

咱们将经过重构一个 mocktail (一种不含酒精的鸡尾酒)选择程序来探索它是如何工做的,即便咱们选择相同的 mocktail 两次也会更新。react

咱们的 mocktail 选择程序

目录结构以下所示:bash

src
 |-> App.js
 |-> Mocktail.js
 |-> index.js
 |-> index.css
 |-> Spinner.js
复制代码复制代码

咱们的程序如何工做

咱们的程序将显示一个被选中的 mocktail。能够经过单击按钮来选择或切换 mocktail。这时会加载一个新的 mocktail,并在加载完成后渲染出这个 mocktail 的图像。app

App 组件的父组件有 mocktail 状态和 updateMocktail 方法,用于处理更新 mocktail。函数

import React, { Component } from 'react';

import Mocktail from './Mocktail';post

class App extends Component {性能

state = { mocktail: '' }动画

updateMocktail = mocktail => this.setState({ mocktail })this

render() {spa

<span class="hljs-keyword">const</span> mocktails = [<span class="hljs-string">'Cosmopolitan'</span>, <span class="hljs-string">'Mojito'</span>, <span class="hljs-string">'Blue Lagoon'</span>];

<span class="hljs-keyword">return</span> (
  &lt;React.Fragment&gt;
    &lt;header&gt;
      &lt;h1&gt;Select Your Mocktail&lt;/h1&gt;
      &lt;nav&gt;
        {
          mocktails.map((mocktail) =&gt; {
            return &lt;button 
              key={mocktail}
              value={mocktail}
              type="button"
              onClick={e =&gt; this.updateMocktail(e.target.value)}&gt;{mocktail}&lt;/button&gt;
          })
        }
      &lt;/nav&gt;
    &lt;/header&gt;
    &lt;main&gt;
        &lt;Mocktail mocktail={this.state.mocktail} /&gt;
    &lt;/main&gt;
  &lt;/React.Fragment&gt;
);
复制代码

} }

复制代码<span class="hljs-keyword">const</span> mocktails = [<span class="hljs-string">'Cosmopolitan'</span>, <span class="hljs-string">'Mojito'</span>, <span class="hljs-string">'Blue Lagoon'</span>]; <span class="hljs-keyword">return</span> ( &lt;React.Fragment&gt; &lt;header&gt; &lt;h1&gt;Select Your Mocktail&lt;/h1&gt; &lt;nav&gt; { mocktails.map((mocktail) =&gt; { return &lt;button key={mocktail} value={mocktail} type="button" onClick={e =&gt; this.updateMocktail(e.target.value)}&gt;{mocktail}&lt;/button&gt; }) } &lt;/nav&gt; &lt;/header&gt; &lt;main&gt; &lt;Mocktail mocktail={this.state.mocktail} /&gt; &lt;/main&gt; &lt;/React.Fragment&gt; ); 复制代码export default App; 复制代码复制代码

button 元素的 onClick 事件上调用 updateMocktail 方法,mocktail 状态被传递给子组件 Mocktail

Mocktail 组件有一个名为 isLoading 的加载状态,当其为 true 时会渲染 Spinner 组件。

import React, { Component } from 'react';

import Spinner from './Spinner';

class Mocktail extends Component {

state = {
    <span class="hljs-attr">isLoading</span>: <span class="hljs-literal">false</span>
}

componentWillReceiveProps() {
    <span class="hljs-keyword">this</span>.setState({ <span class="hljs-attr">isLoading</span>: <span class="hljs-literal">true</span> });
    setTimeout(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> 
        <span class="hljs-keyword">this</span>.setState({
            <span class="hljs-attr">isLoading</span>: <span class="hljs-literal">false</span>
        }), <span class="hljs-number">500</span>);
}

render() {

    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.state.isLoading) {
        <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Spinner</span>/&gt;</span></span>
    }

    <span class="hljs-keyword">return</span> (
        &lt;React.Fragment&gt;
            &lt;div className="mocktail-image"&gt;
                &lt;img src={`img/${this.props.mocktail.replace(/ +/g, "").toLowerCase()}.png`} alt={this.props.mocktail} /&gt;
            &lt;/div&gt;
        &lt;/React.Fragment&gt;
    );
}
复制代码

}

复制代码state = { <span class="hljs-attr">isLoading</span>: <span class="hljs-literal">false</span> } componentWillReceiveProps() { <span class="hljs-keyword">this</span>.setState({ <span class="hljs-attr">isLoading</span>: <span class="hljs-literal">true</span> }); setTimeout(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> <span class="hljs-keyword">this</span>.setState({ <span class="hljs-attr">isLoading</span>: <span class="hljs-literal">false</span> }), <span class="hljs-number">500</span>); } render() { <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.state.isLoading) { <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Spinner</span>/&gt;</span></span> } <span class="hljs-keyword">return</span> ( &lt;React.Fragment&gt; &lt;div className="mocktail-image"&gt; &lt;img src={`img/${this.props.mocktail.replace(/ +/g, "").toLowerCase()}.png`} alt={this.props.mocktail} /&gt; &lt;/div&gt; &lt;/React.Fragment&gt; ); } 复制代码export default Mocktail; 复制代码复制代码

Mocktail 组件的 componentWillReceiveProps 生命周期方法中调用 setTimeout,将加载状态设置为 true达 500 毫秒。

每次使用新的 mocktail 状态更新 Mocktail 组件的 props 时,它会用半秒钟显示加载动画,而后渲染 mocktail 图像。

问题

如今的问题是,即便状态没有改变,mocktail 状态也会被更新,同时触发从新渲染 Mocktail 组件。

例如每当单击 Mojito 按钮时,咱们都会看到程序对 Mojito 图像进行了没必要要地从新渲染。 React 16 对状态性能进行了改进,若是新的状态值与其现有值相同的话,经过在 setState 中返回 null 来防止来触发更新。

img

解决方案

如下是咱们将要遵循的步骤,来防止没必要要的从新渲染:

  1. 检查新的状态值是否与现有值相同
  2. 若是值相同,咱们将返回 null
  3. 返回 null 将不会更新状态和触发组件从新渲染

首先,在 app 组件的 updateMocktail 方法中,建立一个名为 newMocktail 的常量,并用传入的 mocktail 值为其赋值。

updateMocktail = mocktail => {  
  const newMocktail = mocktail;    
  this.setState({     
    mocktail  
  })  
}
复制代码复制代码

由于咱们须要基于以前的状态检查和设置状态,而不是传递 setStateobject,因此咱们须要传递一个之前的状态做为参数的函数。而后检查 mocktail 状态的新值是否与现有值相同。

若是值相同,setState 将返回 null。不然 setState 返回更新的 mocktail 状态,这将触发使用新状态从新渲染 Mocktail 组件。

updateMocktail = mocktail => {
  const newMocktail = mocktail;  
  this.setState(state => {
    if (state.mocktail === newMocktail) {
      return null;
    } else {
      return { mocktail };
    }  
  })  
}
复制代码复制代码

img

如今单击按钮仍会加载其各自的 mocktail 图像。可是,若是咱们再次单击同一个mocktail按钮,React 不会从新渲染 Mocktail 组件,由于 setState 返回 null,因此状态没有改变,也就不会触发更新。

我在下面的两个 GIF 中突出显示了 React DevTools 中的更新:

没有从 setState 返回 null

没有从 setState 返回 null

从 setState 返回 null 以后

从 setState 返回 null 以后

**注意:**我在这里换了一个深色主题,以便更容易观察到 React DOM 中的更新。

总结

本文介绍了在 React 16 中怎样从 setState 返回 null。我在下面的 CodeSandbox 中添加了 mocktail 选择程序的完整代码,供你使用和 fork。

CodeSandbox:codesandbox.io/embed/vj8wk…

经过使用 null 能够防止没必要要的状态更新和从新渲染,这样使咱们的程序执行得更快,从而改善程序的用户体验。

转自在 React 16 中从 setState 返回 null 的妙用

相关文章
相关标签/搜索