【译】函数式的 setState 是 React 的未来

栏目: 服务器 · 发布时间: 6年前

内容简介:本文翻译自:译者注:昨天自己有遇到一个 setState 的坑,就是前一篇文章里记录的,上网 Google 了下看到这一篇,关于 setState,这篇文章讲解的很详细深入:+1:,所以翻译到掘金来,让更多人可以看到。更新:我在React Rally上就此主题进行了后续讨论。虽然这篇文章更多的是关于“函数式的 setState”模式,但更多的是关于深入理解setState。
【译】函数式的 setState 是 React 的未来

本文翻译自: Functional setState is the future of React – freeCodeCamp.org

译者注:昨天自己有遇到一个 setState 的坑,就是前一篇文章里记录的,上网 Google 了下看到这一篇,关于 setState,这篇文章讲解的很详细深入:+1:,所以翻译到掘金来,让更多人可以看到。

更新:我在React Rally上就此主题进行了后续讨论。虽然这篇文章更多的是关于“函数式的 setState”模式,但更多的是关于深入理解setState。

我在React Rally 上关于 setState 做的一个分享 Justice Mba - Demystifying setState() - YouTube

React在JavaScript中推广了函数式编程,这导致了大量的框架采用了React使用的基于组件的UI模式。如今,函数式热潮正在蔓延到整个网络开发生态系统中。

【译】函数式的 setState 是 React 的未来

译者注:上述内容翻译如下:

JavaScript生态系统正在从“本周的新框架”转变为“新的(更快的)本周的React克隆”

ReactJS新闻@ReactJSNews

阿里巴巴发布了他们自己的React-like。 显着更小,更快 - 肯定有一个缺点! github.com/alibaba/rax

但React团队远没有放松。他们继续深入挖掘,探索更多的函数式宝石。

所以今天我向你透露一个隐藏在React中的新功能 - Functional setState!

好吧,这个名字只是我刚刚编造的......而且这并不是全新的或秘密。不,不完全是。其实它是React内置的一种模式,只有很少有开发人员知道这种模式。 它从来没有名字,但现在它确实 - Functional setState!

通过Dan Abramov描述这种模式的话,Functional setState就是一种这样的模式:

“与组件类分开声明状态更改。”

咦?

好吧......这些是你已经知道的了

React是一个基于组件的UI库。组件基本上是一个接受一些属性并返回UI元素的函数。

function User(props) {
  return (
    <div>A pretty user</div>
  );
}
复制代码

组件可能需要拥有并管理其状态。在这种情况下,您通常将组件编写为类。然后你的状态存在于类的 constructor 函数中:

class User {
  constructor () {
    this.state = {
      score : 0
    };
  }
  render () {
    return (
      <div>This user scored {this.state.score}</div>
    );
  }
}
复制代码

为了管理状态,React提供了一个名为 setState() 的特殊方法。你这样使用它:

class User {
  ... 
  increaseScore () {
    this.setState({score : this.state.score + 1});
  }
  ...
}
复制代码

请注意 setState() 的工作原理。您传递一个包含要更新的状态部分的对象。换句话说,您传递的对象将具有与组件状态中的键对应的键,然后 setState() 通过将对象合并到状态来更新或设置状态。这就是“set-State”

你可能不知道的

还记得我们说的 setState() 的工作原理吗?那么,如果我告诉你可以传递一个函数来代替传递一个对象呢?

是的。 setState() 也接受一个函数。该函数接受组件的先前 state 和 当前的 props,它用于计算并返回下一个 state。如下所示:

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

请注意, setState() 是一个函数,我们将另一个函数传递给它(函数式编程...函数式 setState)。乍一看,代码可能看起来很丑陋,只有设置状态的步骤太多了。但你为什么还得这样做?

为什么要将函数传递给setState?

关键在于,状态更新可能是异步的。

想想调用 setState() 时会发生什么。React将首先将传递给 setState() 的对象合并到当前状态。然后它将开始合并。它将创建一个新的React Element树(UI的对象表示),将新树与旧树进行区分,根据传递给 setState() 的对象找出已更改的内容,然后最终更新DOM。 呼!

这么多工作!实际上,这甚至是一个过于简化的摘要。但是相信React!

React does not simply “set-state”.

由于涉及的工作量很大,调用 setState() 可能不会立即更新您的状态。

React可以将多个 setState() 的调用批处理成单个更新来提高性能。

上面这句话是什么意思?

首先,“多次调用 setState() ”可能意味着在一个函数内多次调用 setState() ,如下所示:

state = {score : 0};
// multiple setState() calls
increaseScoreBy3 () {
 this.setState({score : this.state.score + 1});
 this.setState({score : this.state.score + 1});
 this.setState({score : this.state.score + 1});
}
复制代码

现在,当React遇到“多次调用 setState() ”,而不是整整三次执行“set-state”时,React将避免我上面描述的大量工作并巧妙地对自己说:“不! 我不打算三次爬山,在每次旅行中携带和更新一些状态。不,我宁愿得到一个容器,将所有这些切片包装在一起,只需更新一次。“这就是批处理!

请记住,传递给 setState() 的是一个普通对象。现在,假设任何时候React遇到“多次调用 setState() ”,它通过提取传递给每个 setState() 调用的所有对象来完成批处理,将它们合并在一起形成一个对象,然后使用该单个对象来执行 setState()

在JavaScript中,合并对象可能如下所示:

const singleObject = Object.assign(
  {}, 
  objectFromSetState1, 
  objectFromSetState2, 
  objectFromSetState3
);
复制代码

这种模式称为对象组合。

在JavaScript中,“合并”或组合对象的方式是:如果三个对象具有相同的键,则传递给Object.assign()的最后一个对象的键值将作为该键最终的值。例如:

const me  = {name : "Justice"}, 
      you = {name : "Your name"},
      we  = Object.assign({}, me, you);
we.name === "Your name"; //true
console.log(we); // {name : "Your name"}
复制代码

因为 you 是合并到 we 的最后一个对象,所以 you 对象中的 name 值 - “Your name” - 将覆盖 me 对象中 name 的值。

因此,如果使用对象作为参数多次调用 setState() ——每次传递一个对象——React将合并。换句话说,它将用我们传递的多个对象中组成一个新对象。 如果任何对象包含相同的键,则存储具有相同键的最后一个对象的键的值。对吗?

这意味着,鉴于我们上面的 increaseScoreBy3 函数,函数的最终结果将只是 1 而不是 3 ,因为 React 没有立即按我们调用 setState() 的顺序更新状态。首先,React将所有对象组合在一起,结果如下: {score:this.state.score + 1} ,然后只使用新组合的对象进行“set-state”一次。 像这样: User.setState({score:this.state.score + 1}

To be super clear, passing object to setState() is not the problem here. The real problem is passing object to setState() when you want to calculate the next state from the previous state. So stop doing this. It’s not safe!

Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state. Here is a pen by Sophia Shoemaker that demos this problem. Play with it, and pay attention to both the bad and the good solutions in this pen:

要非常清楚,将对象传递给 setState() 不是问题所在。真正的问题是当你想要从前一个状态计算下一个状态时,将对象传递给 setState() 。所以停止这样做。这不安全!

因为 this.propsthis.state 可以异步更新,所以不应该依赖它们的值来计算下一个状态。

索菲亚·舒梅克(Sophia Shoemaker)的这个例子可以演示这个问题。 演示它,并注意这个例子中的坏和好的解决方案。

函数式setState解决了我们的问题

如果你没有花时间演示上面的例子,我强烈建议你还是先看一下,因为它将帮助你掌握这篇文章的核心概念。

当你演示了上面的例子,你无疑看到函数式setState解决了我们的问题。但究竟是怎么做的呢?

我们来咨询React的核心成员 - Dan。

【译】函数式的 setState 是 React 的未来
Dan的twitter

请注意他给出的答案。

当你使用函数式setState ...

更新将被放进一个队列,然后按调用顺序执行。

因此,当React遇到“多次函数式 setState() 调用”时,React按照“调用它们的顺序”对函数进行排队,而不是将对象合并在一起,(当然没有要合并的对象)。

之后,React继续通过调用“队列”中的每个函数来更新状态,将它们传递给先前的状态 - 即,在第一个函数setState()调用之前的状态(如果当前是第一个函数setState()正在执行)或队列中前一个函数setState()调用的最新更新的状态。

下面我们将来模拟一个setState()方法,这是为了让你了解React正在做什么。另外,为了减少冗长,我们将使用ES6。如果需要,您随时可以编写ES5版本。

首先,让我们创建一个组件类。然后,在其中,我们将创建一个假的setState()方法。此外,我们的组件将具有increaseScoreBy3()方法,该方法将执行多功能setState。最后,我们将实例化该类,就像React所做的那样。

class User{
  state = {score : 0};
  //let's fake setState
  setState(state, callback) {
    this.state = Object.assign({}, this.state, state);
    if (callback) callback();
  }
  // multiple functional setState call
  increaseScoreBy3 () {
    this.setState( (state) => ({score : state.score + 1}) ),
    this.setState( (state) => ({score : state.score + 1}) ),
    this.setState( (state) => ({score : state.score + 1}) )
  }
}
const Justice = new User();
复制代码

请注意,setState还接受可选的第二个参数 - 回调函数。如果它存在,React在更新状态后调用它。

现在,当用户触发 increaseScoreBy3() 时,React会将多个函数式setState放入队列。我们不会在这里伪造这种逻辑,因为我们的重点是 什么才真的使功能setState安全 。但是你可以把“排队”过程的结果想象成一个函数数组,如下所示:

const updateQueue = [
  (state) => ({score : state.score + 1}),
  (state) => ({score : state.score + 1}),
  (state) => ({score : state.score + 1})
];
复制代码

最后,让我们来模拟更新过程:

// recursively update state in the order
function updateState(component, updateQueue) {
  if (updateQueue.length === 1) {
    return component.setState(updateQueue[0](component.state));
  }
return component.setState(
    updateQueue[0](component.state), 
    () =>
     updateState( component, updateQueue.slice(1)) 
  );
}
updateState(Justice, updateQueue);
复制代码

没错,这不是一个很棒的代码。我相信你可以做得更好。但这里的关键焦点是每次React执行函数setState中的函数时,React都会通过向其传递更新状态的新副本来更新您的状态。这使得函数setState可以基于先前的状态设置状态。 在这里,我用完整的代码创建了一个bin。

修补它(可能使它看起来更性感),只是为了更好地理解它。

class User{
  state = {score : 0};
  //fake setState
  setState(state, callback) {
    console.log("state", state);
    this.state = Object.assign({}, this.state, state);
    if (callback) callback();
  }
}

const Justice = new User();

const updateQueue = [
  (state) => ({score : state.score + 1}),
  (state) => ({score : state.score + 1}),
  (state) => ({score : state.score + 1})
];

// recursively update the state in the order
function updateState(component, updateQueue) {
  if (updateQueue.length === 1) {
    return component.setState(updateQueue[0](component.state));
  }

  return component.setState(
    updateQueue[0](component.state), 
    () =>
     updateState( component, updateQueue.slice(1)) 
  );
}
复制代码

运行一下这段代码,确保你看懂它。当你回来时我们会看到是什么让函数式的setState真正变得闪闪发光。

这个秘诀我只告诉你哦

到目前为止,我们已经深入探讨了为什么在React中执行多个函数式setStates是安全的。但是我们实际上还没有完成函数式setState的完整定义:“声明状态更改与组件类分开”。

多年来,setting-state的逻辑——即我们传递给setState()的函数或对象 - 总是存在于组件类中。这比声明更为必要。

那么今天,我向你展示新出土的宝藏 - 最好的React秘密:

【译】函数式的 setState 是 React 的未来
这条推的地址

感谢Dan Abramov!

这是函数式setState的强大功能。在组件类之外声明状态更新逻辑。然后在组件类中调用它。

// outside your component class
function increaseScore (state, props) {
  return {score : state.score + 1}
}
class User{
  ...
// inside your component class
  handleIncreaseScore () {
    this.setState( increaseScore)
  }
  ...
}
复制代码

这是声明性的!您的组件类不再关心状态更新。它只是声明它想要的更新类型。

要深刻理解这一点,请考虑那些通常具有许多状态切片的复杂组件,在不同操作更新每个切片。有时,每个更新功能都需要多行代码。所有这些逻辑都将存在于您的组件中。但以后不再是这样了!

另外,如果你像我一样,我喜欢让每个模块都尽可能短,但现在你觉得你的模块太长了。现在,您可以将所有状态更改逻辑提取到其他模块,然后导入并在组件中使用它。

import {increaseScore} from "../stateChanges";
class User{
  ...
  // inside your component class
  handleIncreaseScore () {
    this.setState( increaseScore)
  }
  ...
}
复制代码

现在,您甚至可以在另一个组件中重用increaseScore函数。只需导入它。

你还可以用函数式setState做什么?

让测试变得简单!

【译】函数式的 setState 是 React 的未来
这条推的地址

你也可以传递额外的参数来计算下一个状态(这个让我大吃一惊...... )

【译】函数式的 setState 是 React 的未来
这条推的地址

期待更多......

多年来,React团队一直在探索如何最好地实现有状态的函数。 函数式setState似乎正是正确的答案(可能)。


以上所述就是小编给大家介绍的《【译】函数式的 setState 是 React 的未来》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

自制编译器

自制编译器

[日] 青木峰郎 / 严圣逸、绝云 / 人民邮电出版社 / 2016-6 / 99.00元

本书将带领读者从头开始制作一门语言的编译器。笔者特意为本书设计了CЬ语言,CЬ可以说是C语言的子集,实现了包括指针运算等在内的C语言的主要部分。本书所实现的编译器就是C Ь语言的编译器, 是实实在在的编译器,而非有诸多限制的玩具。另外,除编译器之外,本书对以编译器为中心的编程语言的运行环境,即编译器、汇编器、链接器、硬件、运行时环境等都有所提及,介绍了程序运行的所有环节。一起来看看 《自制编译器》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具