[翻译&摘抄] React 未来之函数式 setState

原文地址:Functional setState is the future of React
原文作者:Justice Mba
译文出自:掘金翻译计划
原文链接:https://juejin.im/post/58cfcf6e44d9040068478fc6/
译者:reid3290
校对者:sunui,imink

温故知新

1、React 是一个基于组件的 UI 库,组件基本上可以看作是一个接受某些属性然后返回 UI 元素的函数。

2、React 提供了一个用于管理 state 的特殊函数 —— setState()。

3、setState() 的作用机制:你传递给它一个对象,该对象含有 state 中你想要更新的部分。

敲黑板,划重点

setState() 不仅能接受一个对象,还能接受一个函数作为参数。该函数接受该组件前一刻的 state 以及当前的 props 作为参数,计算和返回下一刻的 state。

this.setState(function (state, props) {
 return {
  score: state.score - 1
 }
});

好奇宝宝要问了,这么做的目的是什么?

理由是,state 的更新可能是异步的。

React 在更新 State 的时候发生了什么?》
React 首先会将你传递给 setState() 的参数对象合并到当前 state 对象中,然后会启动所谓的 reconciliation,即创建一个新的 React Element tree(UI 层面的对象表示),和之前的 tree 作比较,基于你传递给 setState() 的对象找出发生的变化,最后更新 DOM。

但是,React 不会仅仅简单地 “set-state”。因为涉及到的工作量比较大,调用 setState() 并不一定会即时更新 state 》React 可能会将多次 setState() 调用批处理(batch)为一次 state 的更新。

也就是说,React 并不会按照 setState() 的调用顺序即时更新 state,而是首先会将所有对象合并到一起,然后仅用该对象进行一次 “set-state”。

如果用户高频多次的setState,React 则会对这些操作进行批处理:即将每次调用 setState() 时传递给它的所有对象合并为一个对象,然后用这个对象去做真正的 setState()。

那么就意味着,下面这个函数执行的结果会是1,而不是我们希望的3。

...

state = {score : 0};

// 多次 setState() 调用
increaseScoreBy3 () {
    this.setState({score : this.state.score + 1});
    this.setState({score : this.state.score + 1});
    this.setState({score : this.state.score + 1});
}

...

需要搞清楚的是,给 setState() 传递对象本身是没有问题的,问题出在当你想要基于之前的 state 计算出下一个 state 时还给 setState() 传递对象。这是不安全的!

让函数式 setState 来完成这样的任务

当你编写函数式 setState 的时候,更新操作会形成一个任务队列,稍后会按其调用顺序依次执行。

核心概念是,使用函数式 setState,你可以传递一个函数作为其参数,当执行该函数时,React 会将更新后的 state 复制一份并传递给它,这便起到了更新 state 的作用。基于上述机制,函数式 setState 便可基于前一刻的 state 来更新当前 state。

更进一步

函数式 setState 的强大之处 —— 在组件类外部声明 state 的更新逻辑,然后在组件类内部调用之:

// 在组件类之外
function increaseScore (state, props) {
  return {score : state.score + 1}
}

class User{
  ...

// 在组件类之内
  handleIncreaseScore () {
    this.setState(increaseScore)
  }

  ...
}

这就叫做 declarative宣告式編程)!组件类不用再关心 state 该如何更新,它只须声明它想要的更新类型即可。

设想如下场景:你有一些很复杂的组件,每个组件的 state 都由很多小的部分组成,基于 action 的不同,你必须更新 state 的不同部分,每一个更新函数都有很多行代码,并且这些逻辑都存在于组件内部。不过有了函数式 setState,再也不用面对上述问题了!

并且,基于函数式 setState,你就可以将 state 的更新逻辑抽离为一个模块,然后在组件中引入和使用该模块。让代码显得小而美。

import {increaseScore} from "../stateChanges";

class User{
  ...

  // 在组件类之内
  handleIncreaseScore () {
    this.setState(increaseScore)
}

  ...
}
相关文章
相关标签/搜索