您的位置:首页 > Web前端 > React

对初学者的React Hooks-关于useState和useEffect的大脑友好指南

2020-08-20 22:56 1201 查看

"What the heck are hooks?"

“钩到底是什么?”

I found myself asking this just as I thought I had covered all the basis of React. Such is the life of a frontend developer, the game is always changing. Enter Hooks.

我以为自己已经涵盖了React的所有基础,就问自己。 这就是前端开发人员的生活,游戏总是在变化。 输入挂钩。

It's always nice to learn something new right? Of course! But sometimes we have to ask ourselves "Why? What's the point in this new thing? Do I have to learn it"?

学习新知识总是很高兴吗? 当然! 但是有时候我们不得不问自己:“为什么?这个新事物有什么意义?我必须学习吗?”?

With hooks, the answer is "not right away". If you have been learning React, and have been using class-based components to date, there is no rush to move to hooks. Hooks are optional and can work in tandem with your existing components. Don't you hate it when you have to rewrite your entire codebase to get some new thing to work?

使用钩子,答案是“不立即”。 如果您一直在学习React,并且迄今为止一直在使用基于类的组件,那么就不必急于转向钩子。 挂钩是可选的,可以与现有组件配合使用。 当您不得不重写整个代码库以使一些新东西起作用时,您是否讨厌它?

Anyway, here are some reasons why hooks were introduced in the first place and why I recommend beginners should learn them.

无论如何,这是为什么首先引入钩子的一些原因,以及为什么我建议初学者学习钩子的原因。

在功能组件中使用状态 (Using state in functional components)

Before hooks, we could not use state in functional components. That means if you have a nicely crafted and tested functional component that suddenly needs to store state, you are stuck with the painful task of refactoring your functional component into a class component.

在使用钩子之前,我们不能在功能组件中使用状态。 这意味着,如果您有一个经过精心设计和测试的功能组件,突然需要存储状态,那么您就将繁重的任务重构为一个类组件。

Hurray! Allowing state within functional components means we don't have to refactor our presentation components Check out this article for more.

欢呼! 内部功能组件意味着我们不必重构我们的介绍部分,以国家看看这篇文章更多

类组件笨拙 (Class components are clunky)

Let's face it, class components come with a lot of boilerplate. Constructors, binding, using "this" everywhere. Using functional components removes a lot of this, so our code becomes easier to follow and maintain.

面对现实,类组件附带了许多样板。 构造函数,绑定,到处都使用“ this”。 使用功能组件消除了很多麻烦,因此我们的代码变得更易于遵循和维护。

You can read more about this on the React docs:

您可以在React文档中阅读更多有关此的内容:

更具可读性的代码 (More readable code)

Since hooks let us use functional components, it means there's less code compared to class components. This makes our code more readable. Well, thats the idea anyway.

由于挂钩允许我们使用功能组件,因此与类组件相比,这意味着更少的代码。 这使我们的代码更具可读性。 好吧,那还是个主意。

We don't have to worry about binding our functions, or remember what "this" relates too, and so on. We can worry about writing our code instead.

我们不必担心绑定功能,也不必记住“ this”之间的关系等等。 我们可以担心编写代码。

If you're just starting out with React, I have a bunch of getting started posts on my blog that might help you out! Check it out here:

如果您刚开始使用React,那么我的博客上有很多入门文章可能会对您有所帮助! 在这里查看:

React状态钩 (React State Hook)

Ah, state. A cornerstone of the React ecosystem. Let's get our feet wet with Hooks by introducing the most common hook that you will be working with -

useState()
.

啊,状态。 React生态系统的基石。 通过介绍将要使用的最常见的钩子

useState()
让我们开始熟悉Hooks。

Let's take a look at a class component that has state.

让我们看一下具有状态的类组件。

import React, { Component } from 'react';
import './styles.css';

class Counter extends Component {
state = {
count: this.props.initialValue,
};

setCount = () => {
this.setState({ count: this.state.count + 1 });
};

render() {
return (
<div>
<h2>This is a counter using a class</h2>
<h1>{this.state.count}</h1>

<button onClick={this.setCount}>Click to Increment</button>
</div>
);
}
}

export default Counter;

With React Hooks, we can rewrite this component and remove a lot of stuff, making it easier to understand:

借助React Hooks,我们可以重写此组件并删除很多内容,从而更易于理解:

import React, { useState } from 'react';

function CounterWithHooks(props) {
const [count, setCount] = useState(props.initialValue);

return (
<div>
<h2>This is a counter using hooks</h2>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>Click to Increment</button>
</div>
);
}

export default CounterWithHooks;

On the face of it there is less code, but what's going on?

从表面上看,代码更少了,但是发生了什么呢?

React状态语法 (React State Syntax)

So we've seen our first hook! Hurrah!

因此,我们已经看到了第一个钩子! 欢呼!

const [count, setCount] = useState();

Basically, this uses destructuring assignment for arrays. The

useState()
function gives us 2 things:

基本上,这使用数组的解构分配。

useState()
函数给我们两件事:

  • a variable to hold the state value, in this case, it's called

    count
    - a function to change the value, in this case, it's called
    setCount
    .

    一个变量保存状态值 ,在这种情况下,它被称为

    count
    - 更改值的函数 ,在这种情况下,它被称为
    setCount

You can name these whatever you want:

您可以根据需要命名这些名称:

const [myCount, setCount] = useState(0);

And you can use them throughout the code like normal variables/functions:

您可以像普通变量/函数一样在整个代码中使用它们:

function CounterWithHooks() {
const [count, setCount] = useState();

return (
<div>
<h2>This is a counter using hooks</h2>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>Click to Increment</button>
</div>
);
}

Notice the

useState
hook at the top. We're declaring/destructuring 2 things:

请注意顶部的

useState
挂钩。 我们要声明/破坏2件事情:

  • counter
    : a value which will hold our state value

    counter
    :将保持我们的状态值的值

  • setCounter
    : a function which will change our
    counter
    variable

    setCounter
    :将更改我们的
    counter
    变量的函数

As we continue through the code, you'll see this line:

在继续执行代码时,您将看到以下行:

<h1>{count}</h1>

This is an example of how we can use a state hook variable. Within our JSX, we place our

count
variable within
{}
to execute it as JavaScript, and in turn the
count
value gets rendered on the page.

这是一个如何使用状态挂钩变量的示例。 在我们的JSX中,我们将

count
变量放在
{}
以将其作为JavaScript执行,然后将
count
数值呈现在页面上。

Comparing this to the old "class-based" way of using a state variable:

将此与使用状态变量的旧的“基于类”的旧方法进行比较:

<h1>{this.state.count}</h1>

You'll notice we no longer need to worry about using

this
, which makes our life a lot easier - for example, the VS Code editor will give us a warning if
{count}
is not defined, allowing us to catch errors early. Whereas it won't know if
{this.state.count}
is undefined until the code is run.

您会注意到我们不再需要担心使用

this
,这使我们的工作变得更加轻松-例如,如果未定义
{count}
,VS Code编辑器将向我们发出警告,从而使我们可以尽早发现错误。 在运行代码之前,它不会知道
{this.state.count}
是否未定义。

On to the next line!

转到下一行!

<button onClick={() => setCount(count + 1)}>Click to Increment</button>

Here, we're using the

setCount
function (remember we destructured/declared this from the
useState()
hook) to change the
count
variable.

在这里,我们使用

setCount
函数(请记住,我们从
useState()
挂钩中
useState()
进行了
setCount
化/声明)来更改
count
变量。

When the button is clicked, we update the

count
variable by
1
. Since this is a change of state this triggers a rerender, and React updates the view with the new
count
value for us. Sweet!

单击按钮后,我们将

count
变量更新
1
。 由于这是状态变化,因此会触发重新渲染,React用新的
count
值为我们更新视图。 甜!

如何设置初始状态? (How can I set the initial state?)

You can set the initial state by passing an argument to the

useState()
syntax. This can be a hardcoded value:

您可以通过将参数传递给

useState()
语法来设置初始状态。 这可以是一个硬编码的值:

const [count, setCount] = useState(0);

Or can be taken from the props:

或者可以从道具中获取:

const [count, setCount] = useState(props.initialValue);

This would set the

count
value to whatever the
props.initialValue
is.

这会将

count
数值设置为
props.initialValue
值。

That sums up

useState()
. The beauty of it is that you can use state variables/functions like any other variable/function you would write yourself.

总结

useState()
。 它的优点在于您可以像使用其他任何自己编写的变量/函数一样使用状态变量/函数。

如何处理多个状态变量? (How do I handle multiple state variables?)

This is another cool thing about hooks. We can have as many as we like in a component:

这是关于钩子的另一件事。 我们可以在一个组件中拥有任意数量的对象:

const [count, setCount] = useState(props.initialValue);
const [title, setTitle] = useState("This is my title");
const [age, setAge] = useState(25);

As you can see, we have 3 seperate state objects. If we wanted to update the age for example, we just call the setAge() function. The same with count and title. We no longer are tied to the old clunky class component way where we have one massive state object stored using setState():

如您所见,我们有3个独立的状态对象。 例如,如果要更新年龄,则只需调用setAge()函数。 计数标题相同。 我们不再局限于旧的笨拙的类组件方式,在这种方式下,我们使用setState()存储了一个大型状态对象:

this.setState({ count: props.initialValue, title: "This is my title", age: 25 })

那么,当道具或状态发生变化时如何更新事物呢? (So, what about updating things when props or state changes?)

When using hooks and functional components, we no longer have access to React lifecycle methods like

componentDidMount
,
componentDidUpdate
, and so on. Oh, dear! Do not panic my friend, React has given us another hook we can use:

当使用钩子和功能组件时,我们不再可以访问React生命周期方法,例如

componentDidMount
componentDidUpdate
等等。 噢亲爱的! 不要惊慌,我的朋友,React给了我们另一个可以使用的钩子:

  • Drum Roll *

    鼓卷 *

输入useEffect! (Enter useEffect!)

The Effect hook (useEffect()) is where we put "side effects".

效果钩子( useEffect() )是放置“副作用”的地方。

Eh, side effects? What? Let's go off-track for a minute and discuss what a side effect actually is. This will help us understand what

useEffect()
does, and why it's useful.

嗯,有副作用吗? 什么? 让我们走一分钟,讨论实际上是什么副作用。 这将帮助我们了解

useEffect()
作用以及它的用处。

A boring computer-y explanation would be.

一个无聊的计算机解释将是。

"In programming, a side effect is when a procedure changes a variable from outside its scope"

“在编程中,副作用是过程从其范围之外更改变量时”

In React-y terms, this means "when a component's variables or state changes based on some outside thing". For example, this could be:

用React-y术语来说,这意味着“当组件的变量或状态因某些外部事物而改变时”。 例如,这可能是:

  • When a component receives new props that change its state

    组件收到更改其状态的新道具时
  • When a component makes an API call and does something with the response (e.g, changes the state)

    当组件进行API调用并对响应执行某些操作时(例如,更改状态)

So why is it called a side effect? Well, we cannot be sure what the result of the action will be. We can never be 100% certain what props we are going to receive, or what the response from an API call would be. And, we cannot be sure how this will affect our component.

那么为什么称之为副作用呢? 好吧, 我们不能确定该操作的结果是什么 。 我们永远无法百分百确定我们将要收到的道具,或API调用的响应。 而且,我们无法确定这将如何影响我们的组件。

Sure we can write code to validate, and handle errors, and so on, but ultimately we cannot be sure what the side effects of said things are.

当然,我们可以编写代码来验证和处理错误等,但是最终我们不能确定所说事物的副作用是什么。

So for example, when we change state, based on some outside thing this is know as a side effect.

因此,例如,当我们更改状态时,基于某些外部因素,这被称为副作用。

With that out of the way, let's get back to React and the useEffect Hook!

顺便说一句,让我们回到React和useEffect Hook!

When using functional components we no longer have access to life cycle methods like

componentDidMount()
,
componentDidUpdate()
etc. So, in effect (pun intended), the useEffect hooks replace the current React Life Cycle hooks.

当使用功能组件时,我们不再可以访问生命周期方法,例如

componentDidMount()
componentDidUpdate()
等。因此,实际上(双关语意),useEffect挂钩代替了当前的React Life Cycle挂钩。

Let's compare a class-based component with how we use the useEffect hook:

让我们将基于类的组件与我们如何使用useEffect挂钩进行比较:

import React, { Component } from 'react';

class App extends Component {
componentDidMount() {
console.log('I have just mounted!');
}

render() {
return <div>Insert JSX here</div>;
}
}

And now using useEffect():

现在使用useEffect():

function App() {
useEffect(() => {
console.log('I have just mounted!');
});

return <div>Insert JSX here</div>;
}

Before we continue, it's important to know that, by default, the useEffect hook runs on every render and re-render. So whenever the state changes in your component or your component receives new props, it will rerender and cause the useEffect hook to run again.

在继续之前,重要的是要知道,默认情况下, useEffect挂钩在每个render和re-render上运行 。 因此,只要组件中的状态发生变化或组件收到新的道具,它将重新渲染并导致useEffect挂钩再次运行。

一次运行效果(componentDidMount) (Running an effect once (componentDidMount))

So, if hooks run every time a component renders, how do we ensure a hook only runs once when the component mounts? For example, if a component fetches data from an API, we don't want this happening every time the component re-renders!

因此,如果挂钩在每次渲染组件时都运行,那么我们如何确保挂钩在挂载组件时仅运行一次? 例如,如果组件从API获取数据,我们不希望每次重新渲染组件时都发生这种情况!

The

useEffect()
hook takes a second parameter, an array, containing the list of things that will cause the useEffect hook to run. When changed, it will trigger the effect hook. The key to running an effect once is to pass in an empty array:

useEffect()
挂钩具有第二个参数,即数组, 其中包含将导致useEffect挂钩运行的事物列表 。 更改后,它将触发效果钩。 一次运行效果的关键是传递一个空数组:

useEffect(() => {
console.log('This only runs once');
}, []);

So this means the useEffect hook will run on the first render as normal. However, when your component rerenders, the useEffect will think "well, I've already run, there's nothing in the array, so I won't have to run again. Back to sleep for me!" and simply does nothing.

因此,这意味着useEffect挂钩将在第一个渲染上正常运行。 但是,当您的组件重新发布时,useEffect会认为“好,我已经运行了,数组中什么也没有,所以我不必再次运行。为我睡吧!” 却什么也没做。

In summary, empty array =

useEffect
hook runs once on mount

总之,空数组=

useEffect
挂钩在安装时运行一次

事物发生变化时使用效果(componentDidUpdate) (Using effects when things change (componentDidUpdate))

We've covered how to make sure a

useEffect
hook only runs once, but what about when our component receives a new prop? Or we want to run some code when the state changes? Hooks let us do this as well!

我们已经介绍了如何确保

useEffect
挂钩仅运行一次,但是当我们的组件收到新的道具时该怎么办? 或者我们想在状态改变时运行一些代码? 挂钩也让我们这样做!

useEffect(() => {
console.log("The name props has changed!")
}, [props.name]);

Notice how we are passing stuff to the useEffect array this time, namely props.name.

注意,这次我们如何将东西传递给useEffect数组,即props.name 。

In this scenario, the useEffect hook will run on the first load as always. Whenever your component receives a new name prop from its parent, the useEffect hook will be triggered, and the code within it will run.

在这种情况下,useEffect挂钩将像往常一样在首次加载时运行。 每当您的组件从其父级收到新名称prop时 ,都会触发useEffect挂钩,并且其中的代码将运行。

We can do the same thing with state variables:

我们可以使用状态变量来做同样的事情:

const [name, setName] = useState("Chris");

useEffect(() => {
console.log("The name state variable has changed!");
}, [name]);

Whenever the

name
variable changes, the component rerenders and the useEffect hook will run and output the message. Since this is an array, we can add multiple things to it:

每当

name
变量更改时,组件就会重新渲染,并且useEffect挂钩将运行并输出消息。 由于这是一个数组,因此我们可以向其中添加多个内容:

const [name, setName] = useState("Chris");

useEffect(() => {
console.log("Something has changed!");
}, [name, props.name]);

This time, when the

name
state variable changes, or the
name prop
changes, the useEffect hook will run and display the console message.

这次,当

name
状态变量更改或
name prop
更改时,useEffect挂钩将运行并显示控制台消息。

我们可以使用componentWillUnmount()吗? (Can we use componentWillUnmount()?)

To run a hook as the component is about to unmount, we just have to return a function from the

useEffect
hook:

要在要卸载的组件上运行钩子,我们只需要从

useEffect
钩子返回一个函数:

useEffect(() => {
console.log('running effect');

return () => {
console.log('unmounting');
};
});

我可以一起使用不同的挂钩吗? (Can I use different hooks together?)

Yes! You can use as many hooks as you want in a component, and mix and match as you like:

是! 您可以在组件中使用任意数量的挂钩,并根据需要混合和匹配:

function App = () => {
const [name, setName] = useState();
const [age, setAge] = useState();

useEffect(()=>{
console.log("component has changed");
}, [name, age])

return(
<div>Some jsx here...<div>
)
}

结论-接下来是什么? (Conclusion - What Next?)

There you have it. Hooks allow us to use good old fashioned JavaScript functions to create simplier React components, and reduce alot of boilerplate code.

你有它。 挂钩使我们能够使用老式JavaScript函数来创建更简单的React组件,并减少大量样板代码。

Now, run off into the world of Reac hooks and try building stuff yourself! Speaking of building stuff yourself...

现在,进入Reac钩子世界,尝试自己动手制作东西! 说到自己建造东西...

翻译自: https://www.freecodecamp.org/news/beginners-guide-to-using-react-hooks/

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: