概述
在 React 16 中为了防止不必要的 DOM 更新,允许你决定是否让 .setState 更来新状态。在调用 .setState 时返回 null 将不再触发更新。
我们将通过重构一个 mocktail (一种不含酒精的鸡尾酒)选择程序来探索它是如何工作的,即使我们选择相同的 mocktail 两次也会更新。
我们的 mocktail 选择程序 目录结构如下所示:
1src
2 |-> App.js
3 |-> Mocktail.js
4 |-> index.js
5 |-> index.css
6 |-> Spinner.js
我们的程序如何工作
我们的程序将显示一个被选中的 mocktail。可以通过单击按钮来选择或切换 mocktail。这时会加载一个新的 mocktail,并在加载完成后渲染出这个 mocktail 的图像。
App 组件的父组件有 mocktail 状态和 updateMocktail 方法,用于处理更新 mocktail。
1import React, { Component } from 'react';
2
3import Mocktail from './Mocktail';
4
5class App extends Component {
6
7 state = {
8 mocktail: ''
9 }
10
11 updateMocktail = mocktail => this.setState({ mocktail })
12
13 render() {
14
15 const mocktails = ['Cosmopolitan', 'Mojito', 'Blue Lagoon'];
16
17 return (
18 <React.Fragment>
19 <header>
20 Select Your Mocktail
21 <nav>
22 {
23 mocktails.map((mocktail) => {
24 return <button
25 key={mocktail}
26 value={mocktail}
27 type="button"
28 onClick={e => this.updateMocktail(e.target.value)}>{mocktail}</button>
29 })
30 }
31 </nav>
32 </header>
33 <main>
34 <Mocktail mocktail={this.state.mocktail} />
35 </main>
36 </React.Fragment>
37 );
38 }
39}
40
41export default App;
在 button 元素的 onClick 事件上调用 updateMocktail 方法,mocktail 状态被传递给子组件 Mocktail。
Mocktail 组件有一个名为 isLoading 的加载状态,当其为 true 时会渲染 Spinner 组件。
1import React, { Component } from 'react';
2
3import Spinner from './Spinner';
4
5class Mocktail extends Component {
6
7 state = {
8 isLoading: false
9 }
10
11 componentWillReceiveProps() {
12 this.setState({ isLoading: true });
13 setTimeout(() =>
14 this.setState({
15 isLoading: false
16 }), 500);
17 }
18
19 render() {
20
21 if (this.state.isLoading) {
22 return <Spinner/>
23 }
24
25 return (
26 <React.Fragment>
27 <div className="mocktail-image">
28 <img src={`img/${this.props.mocktail.replace(/ +/g, "").toLowerCase()}.png`} alt={this.props.mocktail} />
29 </div>
30 </React.Fragment>
31 );
32 }
33}
34
35export 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 值为其赋值。
1updateMocktail = mocktail => {
2 const newMocktail = mocktail;
3 this.setState({
4 mocktail
5 })
6}
因为我们需要基于之前的状态检查和设置状态,而不是传递 setState 和 object,所以我们需要传递一个以前的状态作为参数的函数。然后检查 mocktail 状态的新值是否与现有值相同。
如果值相同,setState 将返回 null。否则 setState 返回更新的 mocktail 状态,这将触发使用新状态重新渲染 Mocktail 组件。
1updateMocktail = mocktail => {
2 const newMocktail = mocktail;
3 this.setState(state => {
4 if (state.mocktail === newMocktail) {
5 return null;
6 } else {
7 return { mocktail };
8 }
9 })
10}
现在单击按钮仍会加载其各自的 mocktail 图像。但是,如果我们再次单击同一个mocktail按钮,React 不会重新渲染 Mocktail 组件,因为 setState 返回 null,所以状态没有改变,也就不会触发更新。
我在下面的两个 GIF 中突出显示了 React DevTools 中的更新:
没有从 setState 返回 null
从 setState 返回 null 之后
注意:我在这里换了一个深色主题,以便更容易观察到 React DOM 中的更新。
总结
本文介绍了在 React 16 中怎样从 setState 返回 null。我在下面的 CodeSandbox 中添加了 mocktail 选择程序的完整代码,供你使用和 fork。
CodeSandbox:https://codesandbox.io/embed/vj8wk0mzjy
通过使用 null 可以防止不必要的状态更新和重新渲染,这样使我们的程序执行得更快,从而改善程序的用户体验。
用户偶然发现我们的产品,他们对产品的看法直接反映了对公司及其产品的看法,因此我们需要以自然和直观的方式围绕用户的期望去构建体验。
希望本文能够对你有所帮助。感谢阅读!
原文:https://blog.logrocket.com/returning-null-from-setstate-in-react-16-5fdb1c35d457/