2019-09-05
JiangRen Mr
看到“reducer”
这个词,容易让人联想到Redux,但是在本文中,不必先理解Redux
才能阅读这篇文章。咱们将一起讨论“reducer
”实际上是什么,以及如何利用useReducer
来管理组件中的复杂状态,以及这个新钩子对Redux意味着什么?
如果你熟悉Redux或数组上中的reduce
方法,你大概就知道“reducer”是什么。 如果不熟悉,“reducer
”大概是一个带有2
个值并返回1
个值的函数这么个意思。
如果你有一系列的东西,并且想将这些东西组合成一个单独的物体。“函数式编程”中就是使用Array的reduce
函数。 例如,如果你有一个数字数组并且想得到数组中所有数字的总和,咱们就可以写一个reducer
函数并将它传递给reduce
,如下所示:
let numbers = [1, 2, 3];
let sum = numbers.reduce((total, number) => {
return total + number;
},0)
如果你以前没用过这个,它可能看起来有点神秘。它所做的是为数组的每个元素调用函数,传入前一个total
和当前元素 number
。无论你返回什么,都会成为新的total
。reduce
的第二个参数(在本例中为0
)是total
的初始值。在本例中,reduce
函数会被调用3
次:
(0, 1)
返回 1
(1, 2)
返回 3
(3, 4)
返回 6
reduce
返回6
,它保存在sum
中。
使用useReducer
又会是什么样的?
各位花了在半篇幅来解释Array的reduce
函数,因为 useReducer
参数与 reduce
相同,并且工作方式基本一样。 useReducer
接收 (state, action) => newState
,并且返回了一个与当前state
成对的dispatch
的方法。 咱们使用 useReducer
来编写上面的求和例子。
useReducer((state, acton) => {
return state + action
}, 0)
useReducer
返回一个包含2
个元素的数组,类似于useState
hook。 第一个是当前状态,第二个是dispatch
方法,如下所示:
const [sum, dispatch] = useReducer((state, action) => {
return state + action
}, 0)
注意,state
可以是任何值,它不一定是一个对象,可以是一个数字,一个数组,或者其他任何类型。
尽管 useReducer
是扩展的 hook, 而 useState
是基本的 hook,但 useState
实际上执行的也是一个 useReducer
。这意味着 useReducer
是更原生的,你能在任何使用 useState
的地方都替换成使用 useReducer
。
import React, { useReducer } from 'react';
function Counter() {
// First render will create the state, and it will
// persist through future renders
const [sum, dispatch] = useReducer((state, action) => {
return state + action;
}, 0);
return (
<>
{sum}
<button onClick={() => dispatch(1)}>
Add 1
</button>
</>
);
}
点击按钮dispatch
一个值为1
的action
,该action
将被添加到当前状态,然后组件使用新的状态重新渲染。
这里故意展示了,派发action
没有遵循Redux的典型模式{type: "INCREMENT BY"、value: 1}
或其他类似的东西。hook 的世界是一个新的世界:值得考虑的是,你是否发现旧的模式有价值并希望保留它们,或者你是否愿意更改它们。
再来看看更接近典型Redux reducer
的例子。创建一个组件来管理购物列表,这里看还会使用另外一个 hook:useRef
。
首先,导入两个 hook
import React, { useReducer, useRef } from 'react';
然后创建一个设置ref
和reducer
的组件。 ref
保存对表单的引用,以便咱们可以提取其值。
function ShoppingList() {
const inputRef = useRef();
const [items, dispatch] = useReducer((state, action) => {
switch (action.type) {
// do something with the action
}
}, []);
return (
<>
<form onSubmit={handleSubmit}>
<input ref={inputRef} />
</form>
<ul>
{items.map((item, index) => (
<li key={item.id}>
{item.name}
</li>
))}
</ul>
</>
);
}
请注意,在这种情况下,咱们的“state
”是一个数组。 咱们通过useReducer
第二个参数将它初始化为一个空数组,并从reducer
函数返回一个数组。
useRef
hook为DOM
节点创建持久引用。 调用useRef
会创建一个空的节点。它返回的对象具有current
属性,因此在上面的示例中,咱们可以使用inputRef.current
访问输入的DOM节点。 如果你熟悉React.createRef()
,则其工作原理非常相似。
但是,useRef
返回的对象不仅仅是一种保存DOM引用的方法。 它可以保存特定于此组件实例的任何值,并且它在渲染之间保持不变。
useRef
可用于创建通用实例变量,就像使用具有this.whatever = value
的React
类组件一样。 唯一的问题是,写入它会被视为“副作用”,因此咱们无法在渲染过程中更改它,需要在useEffect
hook 中才能修改。
回到useReducer
示例
我们用表单来处理用户的输入,按回车提交表彰。 现在来编写handleSubmit
函数,该函数主要做的是将一个项添加到列表中,以及处理reducer
中的 action
。
function ShoppingList() {
const inputRef = useRef();
const [items, dispatch] = useReducer((state, action) => {
switch (action.type) {
case 'add':
return [
...state,
{
id: state.length,
name: action.name
}
];
default:
return state;
}
}, []);
function handleSubmit(e) {
e.preventDefault();
dispatch({
type: 'add',
name: inputRef.current.value
});
inputRef.current.value = '';
}
return (
// ... same ...
);
}
在reducer
函数中主要判断两种情况:一种用于action.type==='add'
的情况,还有就是默认下的情况。
当action.type
为 add
时,它返回一个包含所有旧元素的新数组,以及最后的新元素。
这里有一点需要注意的是,咱们使用数组的length
作为一种自动递增的 ID 方便演示,但是对于一个真正的应用程序来说这是不可靠,因为它可能导致重复的ID和bug。(最好使用uuid这样的库,或者让服务器生成一个惟一的ID!)
当用户在输入框中按Enter键时会调用handleSubmit
函数,因此咱们需要调用preventDefault
以避免在发生这种情况时重新加载整页。 然后dispatch
派发一个 action
。
现在来看看如何从列表中删除项的。
在项目中添加一个删除<button>
,点击该按钮派发 它将发送一个 action type === "remove"
的操作,以及要删除的项的索引。
然后咱们只需要在reducer
中处理该action
function ShoppingList() {
const inputRef = useRef();
const [items, dispatch] = useReducer((state, action) => {
switch (action.type) {
case 'add':
// ... same as before ...
case 'remove':
// keep every item except the one we want to remove
return state.filter((_, index) => index != action.index);
default:
return state;
}
}, []);
function handleSubmit(e) { /*...*/ }
return (
<>
<form onSubmit={handleSubmit}>
<input ref={inputRef} />
</form>
<ul>
{items.map((item, index) => (
<li key={item.id}>
{item.name}
<button
onClick={() => dispatch({ type: 'remove', index })}
>
X
</button>
</li>
))}
</ul>
</>
);
}
试着添加一个功能:添加一个清空列表的按钮。
在<ul>
上方插入一个按钮,并为其提供一个onClick prop
,派发一个action
,type
为“clear
”的动作,并在 reducer
方法执行清空列表的动作。
可以在前面 CodeSandbox的基础上完成。
大部分人看到useReducer
hook, React 现在已经内置了reducer
,它有Context
传递数据,所以可能会想到 Redux
会不会因此就死了,以下是原作者给出的一些看法。
作者不认为useReducer
会杀死Redux
。React Hook 扩展了React
在状态管理方面的能力,所以会让使用 Redux
的情况减少。
Redux
仍然比Context + useReducer
的组合做得更多,它具有Redux DevTools 用于调试,可定制中间件、,以及整个相关库生态系统,当然 Redu x在很多地方都被过度使用了,但它仍然具有强大的功能。
Redux
提供了一个全局存储,可以在其中集中保存应用程序数据。useReducer
本地化到特定组件。但是,没有什么可以阻止咱们使用useReducer
和useContext
构建自己的迷你redux
。如果你想这么做,而且符合你的需要,那就去做吧!
代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。
本文章转载自SegmentFault。
Python零基础入门
2025/02/12 03:14 (Sydney)
Business Analyst产品经理+实习
2025/02/22 02:38 (Sydney)
AI入门与实战:用AI提升学习与工作效率
2025/03/01 05:00 (Sydney)
地址
Level 10b, 144 Edward Street, Brisbane CBD(Headquarter)Level 2, 171 La Trobe St, Melbourne VIC 3000四川省成都市武侯区桂溪街道天府大道中段500号D5东方希望天祥广场B座45A13号Business Hub, 155 Waymouth St, Adelaide SA 5000Disclaimer
JR Academy acknowledges Traditional Owners of Country throughout Australia and recognises the continuing connection to lands, waters and communities. We pay our respect to Aboriginal and Torres Strait Islander cultures; and to Elders past and present. Aboriginal and Torres Strait Islander peoples should be aware that this website may contain images or names of people who have since passed away.
匠人学院网站上的所有内容,包括课程材料、徽标和匠人学院网站上提供的信息,均受澳大利亚政府知识产权法的保护。严禁未经授权使用、销售、分发、复制或修改。违规行为可能会导致法律诉讼。通过访问我们的网站,您同意尊重我们的知识产权。 JR Academy Pty Ltd 保留所有权利,包括专利、商标和版权。任何侵权行为都将受到法律追究。查看用户协议
© 2017-2024 JR Academy Pty Ltd. All rights reserved.
ABN 26621887572