翻译|How to Use the useReducer Hook

原文:How to Use the useReducer Hookjavascript

在全部的新React Hooks,或许仅仅是由于名字,就可能成为使用最多的一个. "reducer"这个单词会让不少人联想起Redux-可是读本文,你没必要事先理解Redux.html

咱们这里要谈的"reducer"实际问题是,如何利用useReducer的优势来管理组件中的复杂状态(state),新的hook对于Redux意味着什么?Redux须要hook吗?(对不起,有点跑题).java

[^译注:结合Redux和useReducer来阐述问题,多是一个很好的出发点, Redux的reducer和useReducer核心都是根据组件dispatch的Action的type,payload来对State对象进行更新.概念是彻底同样的,若是对Redux不是太了解, 能够借助useReducer来理解这个过程. 留给你大脑的转变过程是,若是二者之间的这种相同点存在,能够迁移吗?]react

在本文中,咱们会探讨一下useReducer.在组件中管理复杂state,要比useState的方式厉害的多.git

什么是Reducer?

若是你熟悉Redux,或者数组的reduce方法,你就应该知道reducer 是什么?.若是你不熟悉,"reducer"是一个奇特的单词,表明一个函数接收两个值,返回一个值.程序员

若是有一个数组, 你想把其中的元素组合成单个值,"函数式编程"的作法是使用数组的reduce函数. 例如,若是你有一个数组,元素是数字,你想获得数字的综合, 能够编写一个reducer函数,传递给数组的reduce方法,例如:github

let numbers = [1, 2, 3];
let sum = numbers.reduce((total, number) => {
  return total + number;
}, 0);
复制代码

若是以前没看过这样的用法,可能有点晕. 这里所作的是针对数组的每一个元素调用函数,传递的参数是前一个total和当前的number.函数返回值成为新的total,第二个传递给reduce的参数(在这里是0)就是total的初始值. 在这个例子中,输入的函数将会调用三次:npm

  • 用个(0,1)调用,返回1
  • 用个(1,2)调用,返回3
  • 用个(3,3)调用,返回1
  • reduce返回6,结果存储在sum中.

可是,这和useReducer有什么关系?

我花了半页的篇幅俩解释数组的reduce的缘由是由于,useReducer接受相同的参数,基础的工做是相同的.你传递一个reducer函数和初始值(initial state). reducer接收当前的state和一个action,返回一个新的state.咱们能够写一个相似的合计reducer:编程

useReducer((state,action)=>{
 Return state+action;
},0)
复制代码

那么如何触发这个操做? action是如何输入函数的. 想到这个问题就对了.redux

[^译注:这里的这个问题绝对是学习Redux时,使人最困惑的地方]

useReducer返回有两个元素的数组,相似useState hook. 第一个元素是当前的state,第二个参数是dispatch函数. 实际的代码以下:

const [sum, dispatch] = useReducer((state, action) => {
  return state + action;
}, 0);
复制代码

注意"state"能够是任何值,不必定非要是一个对象. 能够是数字,数组,任何东西.

接着来看一个使用reducer的完整组件实例:

import React, { useReducer } from 'react';

function Counter() {
  // 首次渲染会建立一个state,后续的渲染会保存结果.
  const [sum, dispatch] = useReducer((state, action) => {
    return state + action;
  }, 0);

  return (
    <> {sum} <button onClick={() => dispatch(1)}> Add 1 </button> </> ); } 复制代码

能够在CodeSandbox 试试

能够看到,点击按钮,dispatch一个action,参数是1, 这个值会被加到当前的state上, 以后组件会用新的state(更大的值)来渲染组件.

我能够的把"action"写成这样.没有使用{type:"INCREMENT_BY",value:1}的形式或者其余相似Redux的形式,由于reducer不必定必需要准守Redux的type模式.Hooks的世界是一个全新的世界:这一点很值得考虑,是否能发现旧有模式的价值,并保持它们,仍是使用新的模式.

稍微复杂一点的例子

如今来看一个和典型Redux reducer 很是接近的实例.咱们要建立一个组件管理购物车列表,同时也会使用另外一个hook:useRef

首先导入两个hook:

import React,{useReducer,useRef} from 'react'
复制代码

接着建立组件,设置ref和reducer.ref保留对表单输入的引用,便于咱们获取表单的值(也能够经过组件内部state,传递value,onChange props来获取值,可是用useRef能够很好的展示它的用法)

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

题外话解释一下useRef的用法,以后在返回reducer话题.

useRefhook 可让咱们建立一个DOM元素的持久化引用. 调用useRef会建立一个空的引用(能够传递参数进行初始化).返回的对象有一个current属性,因此在实例中,咱们能够经过inputRef.current来访问DOM元素的输入值. 若是你对React.createRef()很熟悉,这里的工做原理是相同的.

useRef返回的对象不只仅能够承载一个DOM元素的引用,它能够承载作组件内的任何特定值,而且在渲染中保持固定.耳熟! 必须的.

useRef也可用于建立泛型实例化变量,和React 类组件中的this.whatever=value作法同样. 惟一的区别是要写成"side effect"的形式,因此就不能在组件渲染过程当中改变它了-只能在useEffect内部执行. 官方Hook问答 有实例讲解.

回到useReducer的例子

from包装input,在按下Enter键时触发提交函数. 如今须要编写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',默认分支:其余的任务.

当reduce获取到"add" action 之后, 它会返回一个新的数组包含了旧的元素,在末尾添加新的一条项目.

咱们使用数组的长度做为自增ID.在这个实例中用自增ID是能够的,可是在实际的app中,不太理想,由于有可能致使重复的ID和bugs(最好是使用相似uuid的软件包,或者由服务器生成一个惟一的ID!)

在用户点击Enter键时,会调用handleSubmit函数,因此须要调用preventDefault来避免正页面的重载. 以后调用dispatch,参数是action.在app中,咱们想让action更像Redux形式-拥有type属性,附带一些数据. 此外还有清除输入.

这个阶段的代码CodeSandBox

移除一项

如今添加从列表中移除项目的能力

挨着项目添加 删除按钮,点击时会dispatch一个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>
    </>
  );
}
复制代码

这个阶段的代码CodeSandBox

练习:清除列表

在额外添加一个内容,清空列表的按钮,做为练习.

<ul>之上添加一个按钮, 添加onClick属性,能够dispatch,type为"clear"的action.以后在reducer中添加分支处理"clear"action.

那么... Redux就此终结篇章了吗?

不少人初次看到useReducer就想,React如今内置reducer了,还有Context能够在全局范围传递数据,因此Redux已死! 我想给出个人一些想法,由于我猜你也很想知道到Redux的命运将会如何?

[^译注: 我我的观点, useReducer的引入不只不会让Redux很难堪,反而会让程序员借助useReducer对Redux有更深的认识,Redux的构架学习可能会有不少的回报,此刻若是舍弃React-Native,投入flutter的怀抱, flutter-Redux的就再也不是一个负担了.]

我不认为useReducer会杀死Redux,Context也不会. 我认为这两个方法只是扩展了React state管理的方法范围而已,因此真正的状况是他们会减小使用Redux的用例.

Redux仍然比Context+useReducer所作的工做多得多- Redux有Redux DevTools用于拍错,能够定制化的组件,还有全生态系统的助手软件包.你能够大胆的说,Redux在不少状况下都有点杀鸡用牛刀.可是我认为它仍然是很是强有力的.

Redux提供的全局store可让你集中控制app的data.useReducer是特定组件私有的.使用useReducer,useContext构建一个迷你版的Redux也是彻底可行的. 若是你想作,它们彻底能够知足需求(Twitter上有不少人已经作了,有截图).我我的仍然想念DevTools.

总之-Redux活蹦乱跳的.Hooks不会让Redux过期.

本身尝试一下

一下是几个小的应用,能够用useReducerhook来完成

  • 建一所房子,有一盏灯,按按钮能够调光-关,低亮度,中等亮度,最高亮度
  • 作一个键盘锁,有6个按钮, 正确的顺序会解锁. 真确的按键顺序事先记录在state中, 顺序不正确会充值.
相关文章
相关标签/搜索