在 React 16 中從 setState 返回 null 的妙用

前端先鋒發表於2019-06-04

翻譯:瘋狂的技術宅

原文:blog.logrocket.com/returning-n…

未經允許嚴禁轉載

概述

在 React 16 中為了防止不必要的 DOM 更新,允許你決定是否讓 .setState 更來新狀態。在呼叫 .setState 時返回 null 將不再觸發更新。

我們將通過重構一個 mocktail (一種不含酒精的雞尾酒)選擇程式來探索它是如何工作的,即使我們選擇相同的 mocktail 兩次也會更新。

我們的 mocktail 選擇程式

目錄結構如下所示:

src
 |-> App.js
 |-> Mocktail.js
 |-> index.js
 |-> index.css
 |-> Spinner.js
複製程式碼

我們的程式如何工作

我們的程式將顯示一個被選中的 mocktail。可以通過單擊按鈕來選擇或切換 mocktail。這時會載入一個新的 mocktail,並在載入完成後渲染出這個 mocktail 的影象。

App 元件的父元件有 mocktail 狀態和 updateMocktail 方法,用於處理更新 mocktail。

import React, { Component } from 'react';

import Mocktail from './Mocktail';

class App extends Component {

  state = {
    mocktail: ''
  }

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

  render() {

    const mocktails = ['Cosmopolitan', 'Mojito', 'Blue Lagoon'];

    return (
      <React.Fragment>
        <header>
          <h1>Select Your Mocktail</h1>
          <nav>
            {
              mocktails.map((mocktail) => {
                return <button 
                  key={mocktail}
                  value={mocktail}
                  type="button"
                  onClick={e => this.updateMocktail(e.target.value)}>{mocktail}</button>
              })
            }
          </nav>
        </header>
        <main>
            <Mocktail mocktail={this.state.mocktail} />
        </main>
      </React.Fragment>
    );
  }
}

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 = {
        isLoading: false
    }

    componentWillReceiveProps() {
        this.setState({ isLoading: true });
        setTimeout(() => 
            this.setState({
                isLoading: false
            }), 500);
    }

    render() {

        if (this.state.isLoading) {
            return <Spinner/>
        }

        return (
            <React.Fragment>
                <div className="mocktail-image">
                    <img src={`img/${this.props.mocktail.replace(/ +/g, "").toLowerCase()}.png`} alt={this.props.mocktail} />
                </div>
            </React.Fragment>
        );
    }
}

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 的妙用

相關文章