如何在10分钟内通过React setState()成为专业人士

This article is aimed at people who have already had their first approach to React, and who, as beginners, have doubts about how setState works and how to use it correctly. It should also help mid to senior devs use cleaner and more abstracted ways of setting state, and make higher-order-functions handle and abstract state.

本文针对的是已经对React有第一个方法的人,并且作为初学者,对setState工作方式以及如何正确使用它有疑问。 它还应帮助中高级开发人员使用更简洁抽象的状态设置方法,并使高阶功能处理和抽象状态。

Just read and have fun!

只是阅读并玩得开心!

So grab a cup of coffee and keep reading! ?

因此,请喝杯咖啡并继续阅读! ?

setState()的基本概念 (Basic Concepts of setState( ))

React Components let you split the user interface (UI) into independent, reusable pieces, so you can think about each piece in isolation.

React Components使您可以将用户界面(UI)分成独立的,可重复使用的部分,因此您可以独立考虑每个部分。

Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen.

从概念上讲,组件就像JavaScript函数。 它们接受任意输入(称为“ props”),并返回描述应该在屏幕上显示的内容的React元素。

If you need to give the user the opportunity to input something or in some way change the variables the component is receiving as props, you’ll need setState.

如果您需要让用户有机会输入某些东西或以某种方式更改组件作为道具接收的变量,则需要setState

Whether you declare a Component as a function or a class, it must never modify its own props.

无论您将Component声明为函数还是类,它都绝不能修改自己的道具。

All React Components must act like pure functions with respect to their props. This means functions that never try to change their inputs and always return the same result for the same inputs.

所有React组件 就其道具而言,必须像纯功能一样发挥作用。 这意味着函数从不尝试更改其输入,并且始终为相同的输入返回相同的结果。

Of course, application UIs are dynamic and change over time. That’s why state was created.

当然,应用程序UI是动态的,并且会随着时间而变化。 这就是创建state的原因。

State allows React components to change their output over time in response to user actions, network responses, and anything else, without violating this rule.

State允许React组件随时间更改其输出,以响应用户操作,网​​络响应和其他任何情况,而不会违反此规则。

Components defined as classes have some additional features. Local state is a feature available only to class Components.

定义为类的组件具有一些其他功能。 本地状态是仅对组件类可用的功能。

setState is the API method provided with the library so that the user is able to define and manipulate state over time.

setState是库提供的API方法,以便用户能够随着时间的推移定义和操作状态。

使用setState()时的三个经验法则 (Three Rules of Thumb When Using setState( ))

不要直接修改状态 (Do Not Modify State Directly)
状态更新可能是异步的 (State Updates May Be Asynchronous)

React may batch multiple setState() calls into a single update for performance.

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

Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.

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

You should always do this kind of manipulation with a functional approach, supplying the state and props and returning the new state based on the former.

您应该始终使用功能性方法进行这种操作,提供stateprops并基于前者返回新state

状态更新已合并 (State Updates are Merged)

When you call setState(), React merges the object you provide into the current state.

调用setState() ,React将您提供的对象合并到当前state

In the example below, we’re updating the variable dogNeedsVaccination independently of the other state variables.

在下面的示例中,我们独立于其他state变量来更新变量dogNeedsVaccination

The merging is shallow, so this.setState({ dogNeedsVaccination: true }) leaves the other variables intact, replacing only the value of dogNeedsVaccination.

合并很浅,因此this.setState({ dogNeedsVaccination: true })保持其他变量不变,仅替换dogNeedsVaccination的值。

尊重数据流并避免陈述最大值 (Respect the Data Flow and Avoid State the Max)

Data flows down! Neither parent nor child components can know if a certain component is stateful or stateless, and they shouldn’t care whether it is defined as a function or a class.

数据流下来! 父组件和子组件都无法知道某个组件是有状态的还是无状态的,因此它们不必关心将其定义为函数还是类。

That’s why state is often called local or encapsulated. It is not accessible to any component other than the one that owns and sets it.

这就是为什么state通常被称为局部或封装的原因。 除了拥有和设置它的组件之外,任何其他组件都无法访问它。

When you setState a prop and use it in your component, you’re breaking the flow of the rendering props. If for some reason the prop passed into your component changed in the parent component, the child will not re-render auto-magically ?!

当你setState的道具和你的组件使用它,你打破渲染道具的流动。 如果由于某种原因传递到组件的prop在父组件中发生了变化,则子代将不会自动重新渲染?!

Let’s check an example:

我们来看一个例子:

Here you have a Home Component which is generating a magic number each 1000ms and setting it into its own state.

在这里,您有一个Home组件,该组件每1000ms生成一个幻数并将其设置为自己的state

After that it renders the number and invokes three Child Components (Siblings) that will receive the magic number with the objective of displaying it using three different approaches:

之后,它呈现数字并调用三个Child组件(兄弟姐妹),它们将接收魔术数字,目的是使用三种不同的方法显示该数字:

第一种方法 (First Approach)

Component ChildOfHome is respecting the React props cascade flow and, considering that the objective is only to show the magic number, it’s rendering the props received directly.

组件ChildOfHome尊重React道具的级联流,并且考虑到目标仅是显示幻数,因此它直接呈现了接收到的props

第二种方法 (Second Approach)

Component ChildOfHomeBrother receives the props from its parent and, invoking componentDidMount, sets the magic number into state. Then it renders the state.magicNumber.

组件ChildOfHomeBrother从其父ChildOfHomeBrother接收props ,并调用componentDidMount ,将幻数设置为state 。 然后,呈现state.magicNumber

This example doesn’t work because render() doesn’t know that a prop has changed so it is not triggering the re-rendering of the component. As the component is not re-rendered anymore, componentDidMount is not invoked and the display is not updated.

此示例不起作用,因为render()不知道prop已更改,因此它不会触发组件的重新渲染。 由于不再重新渲染componentDidMount因此不会调用componentDidMount ,也不会更新显示。

第三种方法 (Third Approach)

Usually when we try to make it work using the second approach we think something is missing. Instead of taking a step back we keep on adding stuff to the code to make it work!

通常,当我们尝试使用第二种方法使其工作时,我们认为有些东西丢失了。 我们没有退后一步,而是继续在代码中添加内容以使其正常工作!

So in this third approach we’ve added componentDidUpdate to check if there’s a change in props to trigger the re-rendering of the component. This is unnecessary and leads us to unclean code. It also brings with it performance costs that will be multiplied by the number of times we do this in a big App where we have a lot of chained Components and side effects.

因此,在第三种方法中,我们添加了componentDidUpdate来检查props是否发生变化以触发组件的重新渲染。 这是不必要的,并导致我们产生不干净的代码。 它还带来了性能成本,该性能成本将乘以我们在大型App中执行此操作的次数,而该App中存在许多链接的组件和副作用。

This is wrong unless you need to allow the user to change the prop value received.

除非您需要允许用户更改收到的prop值,否则这是错误的。

If you don’t need to change the prop value, always try to keep things working according to the React flow (First Approach).

如果您不需要更改prop值,请始终尝试根据React流(第一种方法)使事物正常工作。

You can check a working webpage with this example I’ve prepared for you in Glitch. Take a look and have fun ?

您可以使用我在Glitch中为您准备的示例查看正常工作的网页。 看一下玩得开心吗?

Also check out the code in the Home.js and HomeCodeCleaned.js (without the HTML stuff) in my repo about this article.

还要在有关本文的回购中查看Home.jsHomeCodeCleaned.js的代码(无HTML内容)。

如何设置状态 (How to setState)

So at this point I think it’s time to get our hands dirty!

因此,在这一点上,我认为是时候弄脏我们的双手了!

Let’s play a little bit with setState and improve on that! Just follow along and grab another cup of coffee!

让我们玩一下setState并对此进行改进! 只要跟着,再拿一杯咖啡!

Let’s create a small form to update user data:

让我们创建一个小表格来更新用户数据:

Here’s the code for the example above:

这是上面示例的代码:

We are setting state as an object, and there’s no problem because our current state doesn’t depend on our last state.

我们将state设置为对象,这没有问题,因为我们的当前状态不依赖于上一个状态。

What if we create one more form field to introduce and display Last Name?

如果我们再创建一个表单字段来介绍和显示“姓氏”怎么办?

Nice! We’ve abstracted the handleFormChange method to be able to handle all the input fields and setState.

真好! 我们已经抽象出handleFormChange方法,以便能够处理所有输入字段和setState

What if we add a toggle button to mark the data as valid or invalid and a counter to know how many changes we’ve done to the state?

如果我们添加一个切换按钮以将数据标记为有效或无效,并添加一个计数器来知道我们对该状态做了多少更改,该怎么办?

Yeah! We are rocking! We’ve abstracted a lot of stuff!

是的 我们在摇摆! 我们已经提取了很多东西!

Hmmm… Let’s say I do not want a checkbox to control the isValid variable but a simple toggle button.

嗯……假设我不想使用复选框来控制isValid变量,而是想要一个简单的切换按钮。

Let’s also separate the counter handler from this method. It works well, but in more complex situations where React needs to batch/group changes, it’s not a good policy to rely on the this.state.counter variable to add one more. This value can change without you being aware of it.

让我们还将计数器处理程序与此方法分开。 它运行良好,但是在React需要批量/分组更改的更复杂的情况下,依靠this.state.counter变量添加一个不是一个好的策略。 该值可能会更改,而您不会意识到。

We’re using a shallow copy of it at the instant the operation is invoked, and at that certain point in time you don’t know if its value is the one you were expecting or not!

在操作被调用的那一刻,我们正在使用它的浅表副本,在那个特定的时间点,您不知道它的值是否是您所期望的值!

Let’s go a little bit functional!

让我们开始一些功能!

Okay — We’ve lost abstraction because we’ve separated the handlers, but it’s for a good reason!

好的-我们已经失去了抽象,因为我们已经分离了处理程序,但这是有充分理由的!

So at this time we keep the handleFormChange passing an object to the setState API method. But the handleCounter and handleIsValid methods are now functional and start by grabbing the current state and then, depending on that state, changing it to the next one.

因此,此时,我们保持handleFormChange将对象传递给setState API方法。 但是handleCounterhandleIsValid方法现在可以使用,并且首先获取当前状态,然后根据该状态将其更改为下一个状态。

This is the correct way of changing the state of variables that depend on the previous state.

这是更改依赖于先前状态的变量state的正确方法。

What if we want to console.log() state changes of the firstName and lastName input forms each time a change occurs? Let’s give it a try!

如果我们希望每次发生更改时, firstNamelastName输入表单的console.log()状态更改怎么办? 试一试吧!

Nice! Each time the handleFormChange occurs (which means a new key press happened) the logFields() method is invoked and logs the current state into the console!

真好! 每次handleFormChange发生(这意味着发生了新的按键按下)时, logFields()调用logFields()方法并将当前状态记录到控制台中!

Let’s check the browser console:

让我们检查浏览器控制台:

Wait! What happened here folks? The console log is one change before the current form input! Why is this happening?

等待! 亲人们,这里发生了什么? 控制台日志是当前表单输入之前的一项更改! 为什么会这样呢?

setState是异步的! (setState is async!!)

We already knew this but now we’re seeing it with our eyes! What’s happening there? Let’s take a look at the handleFormChange and logFields methods above.

我们已经知道这一点,但是现在我们可以用眼睛看到它了! 那里发生了什么事? 让我们看一下上面的handleFormChangelogFields方法。

So the handleFormChange method receives the event name and value, then does a setState of this data. Then it calls the handleCounter to update the counter info, and in the end invokes the logFields method. The logFields method grabs the currentState and returns ‘Eduard’ instead of ‘Eduardo’.

因此, handleFormChange方法接收事件名称和值,然后对该数据进行setState 。 然后,它调用handleCounter更新计数器信息,最后调用logFields方法。 logFields方法获取currentState并返回“ Eduard”而不是“ Eduardo”。

The thing is: setState is async and doesn’t act in the moment. React is doing its job and executes the logFields method first, leaving setState for the next event loop.

事实是: setState是异步的,暂时不起作用。 React正在完成其工作,并首先执行logFields方法,将setState留给下一个事件循环。

But how can we avoid this kind of situation?

但是,如何避免这种情况呢?

Well, the setState API has a callback to avoid this situation:

好吧, setState API有一个callback来避免这种情况:

If we want the logFields() to take into account the recent changes we’ve made to the state, we need to invoke it inside the callback, like this:

如果我们希望logFields()考虑到对状态所做的最近更改,则需要在回调内部调用它,如下所示:

Okay, now it’s working!

好的,现在可以了!

We’re telling React: “Hey React! Beware that when you invoke the logFields method I want you to have the state already updated okay? I trust you!”

我们告诉React:“嗨,React! 请注意,当您调用logFields方法时,我希望state已经更新好吗? 我信任你!”

React says: “Okay Edo! I’m going to handle all this batch of stuff I usually do in the backyard with the setState thingy and only when I’m finished with that I’ll invoke logFields()! Cool man! Relax!”

React说:“好的,江户! 我将使用setState来处理通常在后院执行的所有此类工作,只有在完成后,我logFields()调用logFields() ! 帅气的男人! 放松!”

And as a matter of fact — it worked!

事实上,它奏效了!

Okay everyone! By this time we’ve handled the major pitfalls of setState.

大家好! 到这个时候,我们已经解决了setState的主要陷阱。

Do you have the courage to go beyond the wall? Grab a cup of coffee and let’s get really kewl…

您是否有勇气超越壁垒? 喝杯咖啡,让我们真正地草吧……

用setState()看中 (Getting Fancy with setState( ))

Now that we have handleCounter and handleIsValid methods, and setState() expressed with functions, we can compose the state update with other functions! Me likez composition! Let’s have some fun!

现在我们有了handleCounterhandleIsValid方法,并使用函数表示了setState() ,我们可以与其他函数一起handleIsValid状态更新了! 我喜欢作文! 让我们玩得开心!

We can take the logic inside setState to a function outside the class component. Let’s call it toggleIsValid. ☝️

我们可以将setState内部的逻辑带到类组件外部的函数。 让我们将其toggleIsValid 。 ☝️

Now this function can live outside the class component, anywhere in your app.

现在,此功能可以在类组件之外,应用程序中的任何位置使用。

What if we use a higher order function?

如果我们使用高阶函数怎么办?

Wow! Now we’re not invoking the toggleIsValid function anymore. We’re invoking an abstract higher order function called toggleKey and passing a key (string in this case) into it.

哇! 现在,我们不再调用toggleIsValid函数。 我们正在调用一个抽象的高阶函数,名为toggleKey ,并将一个键(在这种情况下为字符串)传递给它。

How do we need to change the toggleIsValid function now?

我们现在需要如何更改toggleIsValid函数?

What?! Now we have a function called toggleKey that receives a key and returns a new function which changes state according to the supplied key.

什么?! 现在我们有了一个名为toggleKey的函数,该函数接收一个key并返回一个新函数,该函数根据提供的键更改状态。

This toggleKey can be in a library or in a helper file. It can be invoked in a lot of different contexts to change the state of whatever you want to its opposite.

toggleKey可以在库中或在帮助文件中。 可以在许多不同的上下文中调用它,以将所需状态更改为相反状态。

Great!

大!

Let’s do the same with the increment counter handler:

让我们对增量计数器处理程序进行相同的操作:

Yeah! It works! So nice. Let’s get crazy now…

是的 有用! 很好。 现在让我们发疯…

射月取回 (Shooting the Moon and Getting Back)

What if we create a generic makeUpdater function that receives the transformation function you want to apply, takes the key, and returns the state function managing the state with the transformation function and the key? Little bit confused? Let’s go!

如果我们创建一个通用的makeUpdater函数来接收要应用的转换函数,获取密钥并返回使用转换函数和密钥管理状态的状态函数,该怎么办? 有点困惑? 我们走吧!

Ok that’s enough…Let’s stop here. ?

好的,足够了……让我们在这里停止。 ?

You can check all the code we’ve done in this GitHub repo.

您可以检查我们在此GitHub存储库中完成的所有代码。

最后但并非最不重要的 (Last But Not Least)

Don’t forget to avoid the max using state and respect React rendering props cascade.

不要忘记避免最大使用状态,并尊重React渲染道具的级联。

Don’t forget setState is async.

不要忘记setState是异步的。

Don’t forget setState can take an object or a function

不要忘记setState可以接受对象或函数

Don’t forget that you should pass a function in when your next state depends on your previous state.

不要忘记,当您的下一个状态取决于您之前的状态时,您应该传递一个函数。

参考书目 (Bibliography)

  1. React Documentation

    React文档
  2. Reach Tech Courses by Ryan Florence, which I really recommend.

    我真的推荐Ryan Florence的技术课程

Thank you very much!

非常感谢你!

翻译自: https://www.freecodecamp.org/news/get-pro-with-react-setstate-in-10-minutes-d38251d1c781/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值