React + ts 实践(三)useReducer实现多组件数据共享
上一章 我们创建了一个todolist,数据什么都是写死,本章我们将要在todoList上面增加对应的功能操作,建议大家下载代码一起配合文章一起来学习,我尽量用图画相结合的方式将结构说明白,下面是本章的代码tags。
在开始本节的实践之前,我现将系统的代码结构示意图展示在此处,方便大家结合代码迅速上手,下面我将todoList的组件结构展示在这里
接下来我们将AppStateContext抽离到单独的文件目录下,我们都放在src/state目录下,具体可以参考gitee上面本章的tag下的代码
1、定义我们的Reducer和修改Action动作
接下来,为了方便我们理解这些操作,我们将TodoStateContext模块构造画图说明一下:
大概是这样的一个构成,上一讲我们用到了useAppState获取我们在TodoStateContext中写入的一些数据及方法,但是作为一个todoList,我们不仅仅考虑的是展示,同时考虑如何添加,因为我们考虑是用useReducer的方式管理数据,通过上一讲我们了解过useReducer的基本用法,话不多说,我们创建action.ts,来定义我们的操作:
import { Action } from "./type"
// 创建任务
export const addTask = (text: string, listId: string): Action => ({
type: 'ADD_TASK',
payload: {
text,
listId
}
})
// 创建列表
export const addList = (text: string): Action => ({
type: 'ADD_LIST',
payload: text
})
下面我们调整调整Reducer方法,这里大家参考我这里的写法
import { nanoid } from "nanoid";
import { Action, AppState } from "./type";
// 调整数据方法
export const appStateReducer = (drap: AppState, action: Action): AppState | void => {
switch (action.type) {
case "ADD_LIST":
drap.lists.push({
id: nanoid(),
text: action.payload,
tasks: []
})
break;
case "ADD_TASK":
const { listId, text } = action.payload
const targetIndex = drap.lists.findIndex((item) => item.id === listId)
drap.lists[targetIndex].tasks.push({
id: nanoid(),
text
})
break
default:
break;
}
}
2、调整AppStateContext
上面我们完成Action及Reducer定义,那么我们要考虑的是如何将这些数据传递给所有组件来去使用,这就得用到我们通过creatContext方式创建出来的数据注入组件AppStateProvider,下面我们就将这些数据及方法挂载到AppStateProvider
import React, { createContext, useContext, Dispatch } from 'react'
import { List, Task, AppState, Action} from './type'
import { useImmerReducer } from 'use-immer'
import { appStateReducer } from './Reducer'
export interface AppStateContextProps {
lists: List[]
getTasksByListId(id: string): Task[]
dispatch: Dispatch<Action>
}
const AppStateContext = createContext<AppStateContextProps>(
{} as AppStateContextProps
)
const appData: AppState = {
lists: [
{
id: "0",
text: "待处理",
tasks: [{ id: "c0", text: "我想去新疆天山" }]
},
{
id: "1",
text: "进行中",
tasks: [{ id: "c2", text: "不好意思居家隔离了" }]
},
{
id: "2",
text: "已完成",
tasks: [{ id: "c3", text: "车票已经作废了" }]
}
]
}
export const AppStateProvider: React.FC<React.PropsWithChildren<{}>> = ({ children }) => {
// 创建我们子组件中用到的数据
const [todoInfo, dispatch] = useImmerReducer(appStateReducer, appData)
const { lists } = todoInfo
const getTasksByListId = (id: string) => {
return lists.find((i: List) => i.id === id)?.tasks || []
}
return (
<AppStateContext.Provider value={{ ...todoInfo, getTasksByListId, dispatch}}>
{children}
</AppStateContext.Provider>
)
}
export const useAppState = () => {
return useContext(AppStateContext)
}
- 1、这里先推荐一个库immerjs ,还有衍生工具库 use-immer;
- 2、Dispatch是react支持的里面的一个泛型函数,定义是这样的
type Dispatch<A> = (value: A) => void
; - 3、关于useImmerReducer是保证了state在使用期间不会被深拷贝,我们的操作都是基于原始数组或者对象上进行的;
3、组件中使用dispatch
组件中我们可以通过获取dispatch方法,将我们的更新方法提供给dispatch的方式进行更新;具体如下:
import React from 'react'
import AddNewItem from './AddNewItem';
import Column from './Columns';
import { useAppState } from '../../state/TodoState/AppStateContext';
import { List } from '../../state/TodoState/type';
import { addList } from '../../state/TodoState/Appction';
const Todo: React.FC = () => {
const { lists, dispatch } = useAppState()
return (
<>
{lists.map((i: List) => (<Column text={i.text} id={i.id} key={`${i.id}${i.text}`} />))}
<AddNewItem
toggleButtonText='添加另一个'
onAdd={(text: string) => dispatch(addList(text))}
></AddNewItem>
</>
);
}
export default Todo
// Column.tsx
import { ColumnContainer, ColumnTitle } from "../../styles"
import Card from "./Card"
import { useAppState } from "../../state/TodoState/AppStateContext"
import { Task } from "../../state/TodoState/type"
import AddNewItem from "./AddNewItem"
import { addTask } from "../../state/TodoState/Appction"
type ColumnProps = {
text: string
id: string
}
const Column: React.FC<ColumnProps> = ({ text, id }) => {
const { getTasksByListId, dispatch } = useAppState()
const tasks = getTasksByListId(id)
return (
<ColumnContainer>
<ColumnTitle>{text}</ColumnTitle>
{
tasks.map((i: Task) => (
<Card id={i.id} text={i.text} key={`${i.id}${i.text}`} />
))
}
<AddNewItem
toggleButtonText='添加下一个任务'
onAdd={(text: string) => dispatch(addTask(text, id))}
></AddNewItem>
</ColumnContainer>
)
}
export default Column
如此我们基本完成了一个通过createContext
加 useReducer
方式创建的多组件之间数据共享,代码看起来比较枯燥,我们通过图画的方式进行解读基本原理:
希望上面的说明能帮助到你,喜欢我的文章就点评赞来一个,三连我也不介意~~ 谢谢各位看官的支持,后面再次附上本章代码tag地址。
转载自:https://juejin.cn/post/7098368072733425677