Hooks使用useReducer

useReducer 是 React 中用于管理复杂状态逻辑的 Hook,尤其适合处理包含多个子值、依赖前一个状态或需要统一管理的场景。

基础用法

要使用 useReducer,你需要从 React 库中导入它,并在组件的顶层调用它。useReducer 接受三个参数:reducer 函数、初始状态和一个可选的初始化函数。它返回一个包含当前状态和 dispatch 函数的数组。

const [state, dispatch] = useReducer(reducer, initialArg, init?)

useReducer接收三个参数

reducer 函数:指定如何更新状态的还原函数,它必须是纯函数,以 state 和 dispatch 为参数,并返回下一个状态。

初始状态:初始状态的计算值。

(可选的)初始化参数:用于返回初始状态。如果未指定,初始状态将设置为 initialArg;如果有指定,初始状态将被设置为调用init(initialArg)的结果。

useReducer返回两个参数

当前的状态:当前状态。在第一次渲染时,它会被设置为init(initialArg)或 initialArg(如果没有 init 的情况下)。

dispatch:调度函数,用于调用 reducer 函数,以更新状态并触发重新渲染。

import React, { useReducer } from 'react';

const initialState = { count: 0 };

//确保 reducer 是纯函数,不直接修改 state,始终返回新对象。
function reducer(state, action) {
//使用 TypeScript 或约定好的字符串常量定义 action.type,避免拼写错误。
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
    </>
  );
}

异步使用

在 useEffect 中 Dispatch:将异步逻辑放在 useEffect 或事件处理函数中,完成后触发 dispatch

const fetchData = async () => {
  dispatch({ type: 'FETCH_START' });
  try {
    const data = await api.get();
    dispatch({ type: 'FETCH_SUCCESS', payload: data });
  } catch (error) {
    dispatch({ type: 'FETCH_ERROR', payload: error });
  }
};

 与 Context API 结合

const AppContext = React.createContext();

const AppProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <AppContext.Provider value={{ state, dispatch }}>
      {children}
    </AppContext.Provider>
  );
};
//子组件中获取reducer值
function Child() {
  const { state, dispatch } = useContext(AppContext);
    return (
      <>
     <button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
     <span>{state.count}</span>
     <button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
     </> 
    );
}

初始状态延迟初始化 

const initState = (initialCount) => ({ count: initialCount });
const [state, dispatch] = useReducer(reducer, initialCount, initState);

useReducer还有第三个参数init,那么它的作用是什么?它也是为了性能优化而来。

有一个场景,计数器的值保存在localStorage里面,进入页面的时候,我们希望从localStorage中读取值来作为useReducer初值,如果没有init,我们可以这样做:

function getInit() {
  const savedCount = localStorage.getItem("count");
  return savedCount ? Number(savedCount) : 0;
}

function counterReducer(state, action) {
  switch (action.type) {
    case "INCREMENT":
      return { count: state.count + 1 };
    case "DECREMENT":
      return { count: state.count - 1 };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(counterReducer, getInit());

  // 使用useEffect来监听状态的变化,并将其保存到localStorage
  useEffect(() => {
    localStorage.setItem("count", state.count);
  }, [state.count]);

  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: "INCREMENT" })}>+1</button>
      <button onClick={() => dispatch({ type: "DECREMENT" })}>-1</button>
    </>
  );
}

 我们直接调用getInit函数作为useReducer的第二个参数,从而得到初始状态。当React初始化这个组件时,它会执行这个函数并使用其返回值作为初始状态。

如果在第三个参数里进行初始化,代码是这样写:

function init(initialValue) {
  // 尝试从localStorage中读取值
  const savedCount = localStorage.getItem("count");
  // 如果有值并且可以被解析为数字,则返回它,否则返回initialValue
  return { count: savedCount ? Number(savedCount) : initialValue };
}

function counterReducer(state, action) {
  switch (action.type) {
    case "INCREMENT":
      return { count: state.count + 1 };
    case "DECREMENT":
      return { count: state.count - 1 };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(counterReducer, 0, init);
  // 使用useEffect来监听状态的变化,并将其保存到localStorage
  useEffect(() => {
    localStorage.setItem("count", state.count);
  }, [state.count]);

  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: "INCREMENT" })}>+1</button>
      <button onClick={() => dispatch({ type: "DECREMENT" })}>-1</button>
    </>
  );
}

最终功能一样的,但它们区别很大: 

1. 执行时机

直接调用函数作为第二个参数:这个函数会在每次组件渲染时执行。使用init函数:init函数只在组件初次渲染时执行一次。

 2.访问到的数据

直接调用函数作为第二个参数:这个函数只能访问到定义它时的作用域内的数据。使用init函数:由于init函数接受initialArg作为参数,这使得init函数具有更大的灵活性,能够基于传入的参数进行计算。

3.性能

直接调用函数作为第二个参数:这个函数执行了复杂计算操作,那么在每次组件渲染时都会执行,可能会导致性能问题。使用init函数:由于它只在组件的初始化阶段执行一次,所以对于那些计算复杂的初始化操作,使用init函数可能会更为高效。

 useState 或 useReducer

简单状态:用 useState

复杂状态流:用 useReducer

useReducer与 Redux 的差异

虽然useReducer和 Redux 都采用了 action 和 reducer 的模式来处理状态,但它们在实现和使用上有几个主要的区别:

范围useReducer通常在组件或小型应用中使用,而Redux被设计为大型应用的全局状态管理工具。

中间件和扩展:Redux支持中间件,这允许开发者插入自定义逻辑,例如日志、异步操作等。而useReducer本身不直接支持,但我们可以模拟中间件的效果。

复杂性:对于简单的状态管理,useReducer通常更简单和直接。但当涉及到复杂的状态逻辑和中间件时,Redux可能更具优势。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值