react钩子_了解用户的React钩子

react钩子

Originally published at https://www.wisdomgeek.com on September 1, 2020.

最初于 2020年9月1日 https://www.wisdomgeek.com 发布

Building upon our React hooks introduction from our previous post on understanding React hooks (useState and useEffect), we will look at the useReducer hook in this post. useReducer hook can be an alternative to useState (in fact, useState uses useReducer internally). Before getting into how to use the useReducer hook, we should understand what is meant by a reducer.

在上一篇关于理解React钩子(useState和useEffect)的文章中对React钩子的介绍基础上,我们将在本文中查看useReducer钩子。 useReducer挂钩可以替代useState(实际上,useState在内部使用useReducer)。 在开始使用useReducer挂钩之前,我们应该了解reducer的含义。

什么是减速器? (What is a reducer?)

If you are coming from a redux background, you can probably skip this section. But for those who are not, let us first understand what a reducer is and what is the need for it. Then we will dive into the useReducer hook.

如果您来自Redux背景,则可以跳过本节。 但是对于那些不是的人,让我们首先了解什么是还原剂,以及对它的需求。 然后,我们将深入使用useReducer挂钩。

The first principle that we should remember before getting into a reducer is that the state is represented as a single immutable tree. So whenever we make a change to state, it is an explicit change. Typically, we use the setState function to make changes to the state. When using redux, we will not be making these changes directly to the state. We will instead be using reducers, which are functions that determine how to change the application state. For making changes to the application state, we will call these reducers with an action to specify what happened.

在进入化简器之前,我们应该记住的第一个原则是,状态被表示为一棵不可变的树。 因此,每当我们更改状态时,这都是一个显式的更改。 通常,我们使用setState函数对状态进行更改。 使用redux时,我们不会直接对状态进行这些更改。 相反,我们将使用reducer,reduces是确定如何更改应用程序状态的函数。 为了更改应用程序状态,我们将通过操作来调用这些化简器以指定发生了什么。

Let us consider a simple counter example:

让我们考虑一个简单的反例:

function Counter({initialCount}) {
const [count, setCount] = useState(initialCount);
return (
<>
Count: {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
</>
);
}

There are two actions that are happening here: increment and decrement. So we can abstract them out into a reducer function that takes an action as an argument:

这里发生两种动作:递增和递减。 因此,我们可以将它们抽象到一个reducer函数中,该函数以一个动作作为参数:

function reducer(count, action) {
switch (action) {
case 'increment':
return count + 1;
case 'decrement':
return count - 1;
}
}

An action is the minimal representation of the change to application data (or state).

动作是对应用程序数据(或状态)更改的最小表示。

为什么需要减速器? (Why do we need reducers?)

The first question that comes to mind is, why do we need reducers or actions? Even though it is redundant in our counter example, but for larger applications, there can be a lot of state-related operations happening everywhere. So, instead of having these spread out all across our application, and inside different components, we move it all into a reducer function. The reducer function then becomes a single source of truth for all application state-related changes. Thus a reducer takes in two arguments, state, and action, and returns the new state of the application.

想到的第一个问题是,为什么我们需要减速器或采取措施? 即使在我们的反例中它是多余的,但对于较大的应用程序,到处都会发生许多与状态相关的操作。 因此,我们没有将它们分散到整个应用程序以及不同的组件内部,而是将其全部移入了reducer函数。 然后,reducer函数将成为所有与应用程序状态相关的更改的唯一事实来源。 因此,化简器接受状态和动作这两个参数,并返回应用程序的新状态。

(state, action) => newState

And all the different actions across the application are now in a single place, and the reducer function updates state according to the action it receives. The reducer is also a pure function, that is it does not have any side-effects.

现在,整个应用程序中的所有不同操作都在一个地方,reduce函数根据收到的操作更新状态。 减速器也是一个纯函数,也就是说它没有任何副作用。

Overall, all these properties of the reducer function make it perfect for testing state changes independently and in isolation. The same input should always return the same output.

总体而言,reduce函数的所有这些属性使其非常适合独立且独立地测试状态变化。 相同的输入应始终返回相同的输出。

减速器功能的作用 (The action in a reducer function)

Even though we touched on the action above, it was a simplified version of what action looks like. Sometimes, we want to pass in a value along with the action as well. If we were to increment by 5 instead of 1, our previous example would require a different action altogether.

即使我们谈到了上面的动作,它还是动作外观的简化版本。 有时,我们也希望将值与操作一起传递。 如果我们要增加5而不是1,那么我们前面的示例将要求完全不同的操作。

Instead, a standard has been laid out for actions. The only requirement is that the action is an object that has a type property defining what the action is. Also, the value of the type property should not be undefined. It can be an object as well, but the best practice is to use a string because strings are serializable. Any additional information can be passed in as different properties.

相反,已经制定了行动标准。 唯一的要求是,操作是一个具有类型属性的对象,该属性定义操作是什么。 同样,type属性的值也不应未定义。 它也可以是一个对象,但是最佳实践是使用字符串,因为字符串是可序列化的。 可以将任何其他信息作为不同的属性传递。

Putting all of this together, our updated reducer would now look like:

综上所述,我们更新后的reducer现在看起来像:

const initialState = {count: 0};
function countReducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
const newState = countReducer(initialState, 'increment') // returns {count: 1}
countReducer(newState , 'decrement') // returns {count: 0}

With all this in place, the component does not need to know anything about updating the state. All the components need to know is that they will dispatch an action with the type of what happened, and the reducer will take care of everything else. Thus we achieve more maintainable code that adheres to the single responsibility principle.

完成所有这些操作后,组件无需了解有关更新状态的任何信息。 所有组件需要知道的是,它们将根据发生的事件来调度动作,而reducer会处理其他所有事情。 因此,我们获得了遵循单一责任原则的更具可维护性的代码。

React的useReducer挂钩 (React’s useReducer hook)

Now that we have an understanding of reducer functions, we can dive into the useReducer hook implementation. As we have already seen, the useReducer hook is useful for managing complex state and state transitions.

既然我们已经了解了reducer的功能,那么我们就可以深入研究useReducer钩子实现。 正如我们已经看到的,useReducer挂钩对于管理复杂的状态和状态转换很有用。

Another benefit of the reducer is to not have to pass props manually around from parent components to child components. This is possible because all the state-related logic is defined inside the reducer function. And the child component only needs to call the reducer function with the appropriate action.

减速器的另一个好处是不必将道具从父级组件手动传递到子级组件。 这是可能的,因为所有与状态相关的逻辑都在reducer函数内部定义。 并且子组件仅需要使用适当的操作来调用reducer函数。

Before we start using the useReducer hook, we need to have the reducer defined. We already did that above for our counter example. Next, we can reduce the useState call with useReducer and pass the reducer to it and the initial state that we want to assign.

在开始使用useReducer挂钩之前,我们需要定义reducer。 对于我们的反例,我们已经在上面做了。 接下来,我们可以使用useReducer简化useState调用,并将reducer传递给它以及我们要分配的初始状态。

const initialState = {count: 0};
const [state, dispatch] = useReducer(reducer, initialState);

Like useState, useReducer returns an array of two variables. The first one refers to the current state of the application, and the second is a dispatch function that we can use to send actions to the reducer. Invoking the dispatch function would change the state of the application, depending on the action that we invoke it with. Thus our counter example would get converted into the following code using the useReducer hook:

与useState一样,useReducer返回两个变量组成的数组。 第一个引用应用程序的当前状态,第二个引用我们可以用来将操作发送给reducer的调度功能。 调用调度功能将更改应用程序的状态,具体取决于我们用其调用的操作。 因此,我们的计数器示例将使用useReducer钩子转换为以下代码:

const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
case 'reset':
return initialState;
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'reset'})}>Reset</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}

It is also important to note that React guarantees that the calls to the dispatch function are stable and will not change on re-renders. Therefore we do not need to put it in the useEffect dependency list.

同样重要的是要注意,React保证对调度功能的调用是稳定的,并且在重新渲染时不会改变。 因此,我们不需要将其放在useEffect依赖项列表中。

将useReducer钩子应用到我们的待办事项列表应用程序 (Applying useReducer hook to our To-Do list application)

Let us now apply the hook to our ToDo list application that we had built in the previous blog post.

现在,让我们将钩子应用到上一篇博客文章中构建的ToDo列表应用程序中。

We will define an items reducer as follows:

我们将定义项简化器,如下所示:

const itemsReducer = (state, action) => {
switch (action.type) {
case 'POPULATE_ITEMS':
return action.items;
case 'ADD_ITEM':
return [...state, action.item];
case 'REMOVE_ITEM':
return state.filter((item) => item !== action.itemToBeDeleted);
default:
return state;
}
};

The three actions correspond to fetching of data, adding an item, and removing an item. These are self-explanatory in what we are trying to do here with respect to the action type that we receive. Next, we will start making use of this reducer in our App component. We will replace useState with our useReducer hook

这三个动作对应于数据获取,添加项目和删除项目。 这些对于我们在此处要针对收到的操作类型进行的尝试是不言自明的。 接下来,我们将开始在我们的App组件中使用此reducer。 我们将用useReducer钩子替换useState

const [items, itemsDispatch] = useReducer(itemsReducer, []);

We can name the first (state) variable whatever we want it to be. It is better to be more explicit about what it refers o since there might be multiple reducers in an application. So we did not name it state as we did in our example before.

我们可以将第一个(状态)变量命名为任意名称。 最好对它的含义更加明确,因为一个应用程序中可能有多个reducer。 因此,我们没有像在前面的示例中那样将其命名为state。

Now that we have access to our state variable and dispatch function, we can use them in our component. Getting into our first useEffect call:

现在我们可以访问状态变量和调度功能,可以在组件中使用它们了。 进入我们的第一个useEffect调用:

useEffect(() => {
const items = JSON.parse(localStorage.getItem('items'));
if (items) {
setItems(items);
}
}, []);

We no longer have access to setItems. But we created an action POPULATE_ITEMS in our reducer to populate the items which can be used here. So we will invoke our dispatch function here instead:

我们不再有权访问setItems。 但是我们在化简器中创建了一个动作POPULATE_ITEMS来填充可在此处使用的项目。 因此,我们将在此处调用调度函数:

useEffect(() => {
const items = JSON.parse(localStorage.getItem('items'));
if (items) {
itemsDispatch({ type: 'POPULATE_ITEMS', items });
}
}, []);

When we invoke this dispatch function, it will invoke our reducer with action type POPULATE_ITEMS. And since we passed in the items (using the shorthand notation), the items reducer returns those items and saves them in the application state.

当我们调用此调度函数时,它将调用动作类型为POPULATE_ITEMS的reducer。 并且由于我们传入了项目(使用简写表示法),因此item reducer返回这些项目并将其保存在应用程序状态中。

For the other useEffect where we were saving items to state, we do not need to do anything since we were not doing any state manipulation.

对于我们将项目保存到状态的另一个useEffect,由于我们没有进行任何状态操作,因此我们不需要执行任何操作。

Next, we will do the same thing for the other actions that we have, that is adding an item and removing an item.

接下来,我们将对其他操作执行相同的操作,即添加一个项目并删除一个项目。

const addItem = (item) => {
// setItems([...items, item]);
// becomes:
itemsDispatch({ type: 'ADD_ITEM', item });
}
const removeItem = (itemToBeDeleted) => {
// setItems(items.filter((item) => itemToBeDeleted !== item));
// becomes
itemsDispatch({ type: 'REMOVE_ITEM', itemToBeDeleted });
};

And that concludes our refactoring to use useReducer hook in our code.

到此,我们的重构结束了在代码中使用useReducer挂钩。

You can find the code changes here and the final code here.

你可以找到的代码改变这里和最终的代码在这里

We will talk about useContext in the next post, and that will finish our to-do application. If there is something else you want us to cover, do drop a comment below to let us know!

我们将在下一篇文章中讨论useContext,这将完成我们的待办事项应用程序。 如果您想让我们介绍其他内容,请在下方添加评论以告知我们!

翻译自: https://medium.com/swlh/understanding-the-usereducer-hook-in-react-f819f0acdfca

react钩子

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值