useReducer和useContext的用法

1,useReducter

作用:useReducer是state的另一种用法,用来管理状态。他避免了大量state的繁杂逻辑,更容易管理。例如以下情况:

import { useState } from 'react';

export default function TaskApp() {
    const [tasks, setTasks] = useState(initialTasks);
    // 新增操作
    function handleAddTask(text) {
        setTasks([
          ...tasks,
          { id: nextId++, text: text },
        ]);
    }
    // 修改操作
    function handleEditTask(task) {
        setTasks(
          tasks.map((t) => {
            if (t.id === task.id) {
              return task;
            } else {
              return t;
            }
          })
        );
    }
    // 删除操作
    function handleDeleteTask(taskId) {
        setTasks(tasks.filter((t) => t.id !== taskId));
    }

  return (
    <>
      <h1>布拉格的行程安排</h1>
      <AddTask onAddTask={handleAddTask} />
      <Task tasks={tasks}
        onChangeTask={handleEditTask}
        onDeleteTask={handleDeleteTask}
      />
    </>
  );
}

一个行数据既可以编辑、删除,又可以新增等多种逻辑操作,useState代码逻辑就会显得臃肿。

使用方法

(1)将设置状态的逻辑(setXXX)修改成dispatch的一个action

主要语法是dicpatch({ type: '具体事件类型', 自定义的其他字段 });

function handleAddTask(text) {
  dispatch({
    type: 'add',
    id: nextId++,
    text: text,
  });
}

function handleEditTask(task) {
  dispatch({
    type: 'edit',
    task: task,
  });
}

function handleDeleteTask(taskId) {
  dispatch({
    type: 'delete',
    id: taskId,
  });
}

(2)编写一个reducer函数,类似于js中封装逻辑的函数。他接受两个参数:state和action

function tasksReducer(tasks, action) {
    switch (action.type) {
        case 'add': {
            return [
                ...tasks,
                {
                  id: action.id,
                  text: action.text
                },
            ];
        }
        case 'edit': {
            return tasks.map((t) => {
                if (t.id === action.task.id) {
                   return action.task;
                } else {
                   return t;
                }
            });
        }
        case 'delete': {
            return tasks.filter((t) => t.id !== action.id);
        }
        default: {
            throw Error('未知 action: ' + action.type);
        }
    }
}

(3)在组件中使用reducer

引入useReducer替换useState:

import { useReducer } from 'react';

初始化reducer替换state:

const [tasks, dispatch] = useReducer(tasksReducer, [
  {id: 0, text: '参观卡夫卡博物馆'},
  // ...
]);

useReducer()的第一个参数tasksReducer就是自定义的Reducer函数

扩展:

reducer同样可以用Immer来简化,首先引入import { useImmerReducer } from 'use-immer';

然后也通过draft参数来修改数据:

function tasksReducer(draft, action) {
  switch (action.type) {
    case 'add': {
      draft.push({
        id: action.id,
        text: action.text
      });
      break;
    }
    case 'edit': {
      const index = draft.findIndex((t) => t.id === action.task.id);
      draft[index] = action.task;
      break;
    }
    case 'delete': {
      return draft.filter((t) => t.id !== action.id);
    }
    default: {
      throw Error('未知 action:' + action.type);
    }
  }
}

2,useContext

作用:

useContext是props传参的另一种用法,他避免了深层嵌套的props和多个相同组件使用的冗余props。例如以下情况:

return (
    <Section level={1}>
        <Heading level={1}>主标题</Heading>
        <Heading level={1}>主标题</Heading>
        <Heading level={1}>主标题</Heading>
    </Section>
)

父组件和多个子组件都要分别传入相同的props“level”,而使用useContext可以解决这种代码冗余

使用方法:

(1)创建Context

import { createContext } from 'react';
const LevelContext = createContext(initial);

(2)使用Context

import { useContext } from 'react'; 
const level = useContext(LevelContext);

(3)使用Context Provider包裹他所有的子组件,并提供Context

// 目标外层组件Section,使用创建的Context值LevelContext.Provider包裹
export default function Section({ level, children }) {
    return (
        <section className="section">
           <LevelContext.Provider value={level}>
              {children}
           </LevelContext.Provider>
        </section>
    );
}

解释:使用第1步创建的Context值LevelContext.Provider来包裹所有的子组件,这样<Section>组件中的任何子组件请求LevelContext时,都从最近的父组件LevelContext.Provider获取传递的value值level。因此useContext同样可以解决深层嵌套,祖孙组件的传值

适用的场景

  • 更改主题(每个子组件样式都要改变)
  • 路由
  • 状态管理(深层的state,且可能需要修改state,通常会与useReducer一起搭配使用)

3,useReducer和useContext结合使用

二者可以配合使用:

(1)初始化useReducer创建两个Context分别对应state和dispatch

// 初始化reducer
import { useReducer } from 'react';
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);


// 创建两个context分别对应state和dispatch
import { createContext } from 'react';
export const TasksContext = createContext(null);
export const TasksDispatchContext = createContext(null);

(2)将state和dispatch放入Context(两个context顺序包裹,为整个组件树提供state和dispatch)

import { TasksContext, TasksDispatchContext } from './TasksContext.js';
// ...
// 包含第一步的初始化reducer逻辑:const [tasks, dispatch] = ...
return (
    <TasksContext.Provider value={tasks}>
        <TasksDispatchContext.Provider value={dispatch}>
          ...
        </TasksDispatchContext.Provider>
    </TasksContext.Provider>
);
// ...

(3)在组件树的任何地方使用context

// 以包裹一个AddTask组件为例
import { TasksContext, TasksDispatchContext } from './TasksContext.js';

export default function TaskApp() {
  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
  // ...
  return (
    <TasksContext.Provider value={tasks}>
      <TasksDispatchContext.Provider value={dispatch}>
        <AddTask />
      </TasksDispatchContext.Provider>
    </TasksContext.Provider>
  );
}

// 在AddTask组件中应用dispatch
import { useContext } from 'react';
import { TasksDispatchContext } from './TasksContext.js';

export default function AddTask() {
  const dispatch = useContext(TasksDispatchContext);
  // ...
  return (
    // ...
    <button onClick={() => {
      dispatch({
        type: 'add',
        id: nextId++,
        text: text,
      });
    }}>Add</button>
}

说明:二者的结合可以在大型应用中大量应用,同时还可以配合部分useState灵活应用

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
useContextuseReducer都是React的Hook函数,用于处理状态管理和组件之间的数据传递。 1. useContext:用于在组件中获取和共享全局状态。它接收一个Context对象作为参数,并返回该Context的当前值。这个值是由最近的上层Context.Provider组件提供的。使用useContext可以避免使用props将状态传递到多个嵌套组件中。 例子: ```javascript const MyContext = React.createContext(); function ParentComponent() { const value = "Hello, useContext!"; return ( <MyContext.Provider value={value}> <ChildComponent /> </MyContext.Provider> ); } function ChildComponent() { const contextValue = React.useContext(MyContext); return <div>{contextValue}</div>; } ``` 2. useReducer:用于管理复杂的状态逻辑。它接收一个reducer函数和初始状态作为参数,并返回当前状态和dispatch函数。reducer函数接收当前状态和action作为参数,并根据action类型来更新状态。使用useReducer可以将状态和对状态的更新逻辑封装在一个函数中,使组件更加清晰和可维护。 例子: ```javascript function reducer(state, action) { switch (action.type) { case 'INCREMENT': return { count: state.count + 1 }; case 'DECREMENT': return { count: state.count - 1 }; default: throw new Error(); } } function Counter() { const [state, dispatch] = React.useReducer(reducer, { count: 0 }); return ( <div> Count: {state.count} <button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button> <button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button> </div> ); } ``` 总结: - useContext用于获取全局状态,避免了props的传递,适用于简单的状态共享。 - useReducer用于管理复杂的状态逻辑,将状态和更新逻辑封装在一个函数中,适用于较为复杂的状态管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妍思码匠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值