一文带你搞懂React中的useReducer

 更新时间:2023年06月16日 09:44:49   作者:何遇er  
useReducer 是除useState之外另一个与状态管理相关的 hook,这篇文章主要为大家介绍了useReducer应用的相关知识,感兴趣的小伙伴可以跟随小编一起学习一下

useReducer 是除useState之外另一个与状态管理相关的 hook,对于熟悉 Redux 的工程师而言,理解 useReducer 将变得简单,在 React 内部,useState 由 useReducer 实现。

useReducer 的类型定义

useReducer 的类型定义如下:

function useReducer<R extends ReducerWithoutAction<any>, I>(
    reducer: R,
    initializerArg: I,
    initializer: (arg: I) => ReducerStateWithoutAction<R>
): [ReducerStateWithoutAction<R>, DispatchWithoutAction];
function useReducer<R extends ReducerWithoutAction<any>>(
    reducer: R,
    initializerArg: ReducerStateWithoutAction<R>,
    initializer?: undefined
): [ReducerStateWithoutAction<R>, DispatchWithoutAction];
function useReducer<R extends Reducer<any, any>, I>(
    reducer: R,
    initializerArg: I & ReducerState<R>,
    initializer: (arg: I & ReducerState<R>) => ReducerState<R>
): [ReducerState<R>, Dispatch<ReducerAction<R>>];
function useReducer<R extends Reducer<any, any>, I>(
    reducer: R,
    initializerArg: I,
    initializer: (arg: I) => ReducerState<R>
): [ReducerState<R>, Dispatch<ReducerAction<R>>];
function useReducer<R extends Reducer<any, any>>(
    reducer: R,
    initialState: ReducerState<R>,
    initializer?: undefined
): [ReducerState<R>, Dispatch<ReducerAction<R>>];

useReducer 的类型定义很复杂,一共有 5 个重载,总体而言,它最多接受3个参数,第 1 个参数是一个用于更新状态的函数,之后将它称为 reducer。第 3 个参数非必填,如果不存在第 3 个参数,那么第 2 个参数将作为状态的初始值;如果存在第 3 个参数,那么它必须是函数,此时第 2 个参数被传递给该函数用于计算状态的初始值,该函数只在组件初始渲染时执行一次。useReducer 的返回值是一个长度为 2 的数组,数组的第 1 个位置是状态值,第 2 个位置是一个用于触发状态更新的函数,将它记为dispatch,调用 dispatch 将导致 reducer 被调用。接下来通过计数器 demo 对比 useState 和useReducer 用法上的差异。

用 useState 实现计数器

用 useState 实现计数器,代码如下:

function UseStateCounterDemo() {
    const [value, setValue] = useState<number>(0)
    const [step, setStep] = useState<number>(1)
    const onChangeStep = (ev: React.ChangeEvent<HTMLInputElement>) => {
        setStep(Number(ev.target.value))
    }
    return (
        <div>
            <h2>用useState实现计数器</h2>
            count: {count}; 
            step: <input type='number' value={step} onChange={onChangeStep}/>
            <button onClick={() => setValue(value + step)}>加</button>
            <button onClick={() => setValue(value - step)}>减</button>
            <button onClick={() => {setValue(0); setStep(1)}}>重置</button>
        </div>
    )
}

上述代码很简单,它使用 useState 定义了两个状态,分别表示计数器的值和加减步数,在过去的文章里已对 useState 做过详细的介绍,这里不再赘述,下面详细介绍用 useReducer 实现计数器。

用 useReducer 实现计数器

定义 reducer

使用 useReducer hook 离不开reducer,它是一个函数,用于更新 useReducer 返回的状态,代码如下:

const initArg: Counter = {value: 0, step: 1}
// 它用于更新state
function reducer(prevState: Counter, action: Action): Counter  {
    switch (action.type) {
        case 'increment':
            return {
                ...prevState,
                value: prevState.value + prevState.step
            }
        case 'decrement':
            return {
                ...prevState,
                value: prevState.value - prevState.step
            }
        case 'reset':
            return initArg
        case 'changeStep':
            return {
                ...prevState,
                step: action.value || initArg.value
            }
        default:
            throw new Error();
    }
}

reducer 用于更新状态,计数器 demo 有两个状态,分别是计数器的当前值和它的加减步数,这两个状态密切相关,这里用一个 TS 接口去描述它,代码如下:

interface Counter {
   // 计数器的当前值
    value: number
   // 计数器的加减步数
    step: number
}

计数器有 3 种操作,分别是加、减、重置和修改步数,这里用 TS 接口描述这些行为,代码如下:

interface Action {
    type: 'increment' | 'decrement' | 'reset'|'changeStep',
    value?: number
}

在组件中使用 useReducer

// 计数器的初始值
const initArg: Counter = {value: 0, step: 1}
function UseReducerCounterDemo() {
    // 使用上一步定义 reducer
    const [counter, dispatch] = useReducer(reducer, initArg)
    const onChangeStep = (ev: React.ChangeEvent<HTMLInputElement>) => {
        dispatch({type: 'changeStep',value: Number(ev.target.value)})
    }
    return (
        <div>
            <h2>用useReducer实现计数器</h2>
            count: {counter.value}; 
            step: <input type='number' value={counter.step} onChange={onChangeStep}/>
            <button onClick={() => dispatch({type: 'increment'})}>加</button>
            <button onClick={() => dispatch({type: 'decrement'})}>减</button>
            <button onClick={() => dispatch({type: 'reset'})}>重置</button>
        </div>
    )
}

只考虑代码量,读者应该都会认为useState比useReducer更简洁。仔细观察可以发现,上述计数器除了有value还有step,step对value有影响,UseStateCounterDemo组件将它们零散地保存在不同的状态中,UseStateCounterDemo组件将它们关联在同一个状态中,内聚性更高。useState与useReducer没有优劣之外,它们有各自适用的场景,这里有如下建议:

1.当状态是一个拥有很多属性的复杂对象,并且状态更新涉及复杂的逻辑时,推荐使用useReducer。

2.当某个状态的更新受另一个状态影响时,推荐使用 useReducer将它们放在一起。

3.当状态只是单独的基本数据类型时,推荐使用 useState。

介绍 useEffect时曾强调,为了在 effect 中拿到状态最新的值,必须给 effect 设置正确地依赖项。在 useEffect 中使用 useReducer 返回的 dispatch 能让 effect 自给自足,减少依赖项。示例代码如下:

function DispatchDemo(props: {step: number}) {
    function reducer(value: number) {
	// 始终能访问到最新的 step 
        return props.step + value
    }
    const [value, dispatch] = useReducer(reducer, 0)
    useEffect(() => {
        document.body.addEventListener('click', dispatch)
        return () => {
            document.body.removeEventListener('click', dispatch)
        }
    }, [])
    return // todo
}

上述代码 useEffect 的第二个参数为空数组,这意味着 effect 只在组件初始渲染时执行。由于React 会让 dispatch 在组件的每次渲染中保持唯一的引用,所以 dispatch 不必出现在effect的依赖中,此特性与 ref 类似。虽然 dispatch 的引用保持不变,但它能调用组件本次渲染时的reducer,在 reducer 作用域将得到最新的 state 和 props。

推荐阅读:

我搞懂了 React 的 useState 和 useEffect

到此这篇关于一文带你搞懂React中的useReducer的文章就介绍到这了,更多相关React useReducer内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • React中DOM事件和状态介绍

    React中DOM事件和状态介绍

    这篇文章主要介绍了React中DOM事件和状态介绍,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下
    2022-08-08
  • 详解在React项目中安装并使用Less(用法总结)

    详解在React项目中安装并使用Less(用法总结)

    这篇文章主要介绍了详解在React项目中安装并使用Less(用法总结),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • 详解React中多种组件通信方式的实现

    详解React中多种组件通信方式的实现

    在React中,组件之间的通信是一个非常重要的话题,React提供了几种方式来实现跨组件通信,下面小编将详细讲讲其中几种通信方式,并提供实际的代码示例,需要的可以参考下
    2023-11-11
  • React中设置样式style方式

    React中设置样式style方式

    这篇文章主要介绍了React中设置样式style方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • react合成事件与原生事件的相关理解

    react合成事件与原生事件的相关理解

    本文主要介绍了react合成事件与原生事件的相关概念,帮助大家区分这两种事件,学习react的同学不妨了解下
    2021-05-05
  • react native实现往服务器上传网络图片的实例

    react native实现往服务器上传网络图片的实例

    下面小编就为大家带来一篇react native实现往服务器上传网络图片的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • sass在react中的基本使用(实例详解)

    sass在react中的基本使用(实例详解)

    这篇文章主要介绍了sass在react中的基本使用,主要包括安装sass和编写APP.tsx中的基本dom,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-09-09
  • Rect Intersection判断两个矩形是否相交

    Rect Intersection判断两个矩形是否相交

    这篇文章主要为大家介绍了Rect Intersection判断两个矩形是否相交的算法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • 解决React报错React.Children.only expected to receive single React element child

    解决React报错React.Children.only expected to rece

    这篇文章主要为大家介绍了React报错React.Children.only expected to receive single React element child分析解决,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • 初识React及React开发依赖详解

    初识React及React开发依赖详解

    React由Facebook来更新和维护,它是大量优秀程序员的思想结晶,React的流行不仅仅局限于普通开发工程师对它的认可,大量流行的其他框架借鉴React的思想,接下来通过本文介绍React基础及React开发依赖介绍,需要的朋友可以参考下
    2022-10-10

最新评论