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灵活应用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

妍思码匠

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

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

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

打赏作者

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

抵扣说明:

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

余额充值