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

52次阅读

共计 3289 个字符,预计需要花费 9 分钟才能阅读完成。

翻译:疯狂的技术宅

原文:https://blog.logrocket.com/re…

未经允许严禁转载

概述

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

我们将通过重构一个 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 来防止来触发更新。

解决方案

以下是我们将要遵循的步骤,来防止不必要的重新渲染:

  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};
    }  
  })  
}


现在单击按钮仍会加载其各自的 mocktail 图像。但是,如果我们再次单击同一个 mocktail 按钮,React 不会重新渲染 Mocktail 组件,因为 setState 返回 null,所以状态没有改变,也就不会触发更新。

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


<center> 没有从 setState 返回 null</center>


<center> 从 setState 返回 null 之后 </center>

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

总结

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

CodeSandbox:https://codesandbox.io/embed/…

通过使用 null 可以防止不必要的状态更新和重新渲染,这样使我们的程序执行得更快,从而改善程序的用户体验。

用户偶然发现我们的产品,他们对产品的看法直接反映了对公司及其产品的看法,因此我们需要以自然和直观的方式围绕用户的期望去构建体验。

希望本文能够对你有所帮助。感谢阅读!


本文首发微信公众号:前端先锋

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章


欢迎继续阅读本专栏其它高赞文章:

  • 深入理解 Shadow DOM v1
  • 一步步教你用 WebVR 实现虚拟现实游戏
  • 13 个帮你提高开发效率的现代 CSS 框架
  • 快速上手 BootstrapVue
  • JavaScript 引擎是如何工作的?从调用栈到 Promise 你需要知道的一切
  • WebSocket 实战:在 Node 和 React 之间进行实时通信
  • 关于 Git 的 20 个面试题
  • 深入解析 Node.js 的 console.log
  • Node.js 究竟是什么?
  • 30 分钟用 Node.js 构建一个 API 服务器
  • Javascript 的对象拷贝
  • 程序员 30 岁前月薪达不到 30K,该何去何从
  • 14 个最好的 JavaScript 数据可视化库
  • 8 个给前端的顶级 VS Code 扩展插件
  • Node.js 多线程完全指南
  • 把 HTML 转成 PDF 的 4 个方案及实现

  • 更多文章 …

正文完
 0