【轉】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 的妙用

相關文章
相關標籤/搜索