上一篇我们讲了项目构建好了之后的一些基础的ts用法,今天我们先跑题,简单的用hooks方式处理一下状态管理,来来来动手搞起来,此处附带上次项目地址,根据具体章节选择合适的版本下载
1、使用useReducer
useReducer可以提供一个类似于穷人版本的Redux功能,那么首先我们看看useReducer的基本用法:
const reducer = (state, action) => newState
const [state, dispatch] = useReducer(reducer, initValues)
// 改变值
dispatch(action)
参考上面的伪代码,我们可知useReducer的一个基本用法:
- 1、初始化通过useReducer注册一个默认的state值,同时注册一个reducer方法用于触发及更新值;
- 2、useReducer返回两个值
state和dispatch一个是值,一个是触发修改值的方法; - 3、通过dispatch将注册的action触发reducer方法进行值state值更新
基于上面我们来一手最基础版本的加减数字的小把戏,参考代码目录src/components/Count/index.tsx,代码如下
import React, { useReducer } from "react"
interface State {
count: number;
}
type ActionType = "increment" | "decrement"
interface Action {
type: ActionType
}
const reducer = (state: State, action: Action) => {
switch (action.type) {
case "increment":
return { count: state.count + 1 }
case "decrement":
return { count: state.count - 1 }
default:
throw new Error()
}
}
const Count: React.FC = () => {
const [state, dispatch] = useReducer(reducer, { count: 0 })
return (
<>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
</>
)
}
export default Count
注意,这里我们可以使用ActionType是通过|吧几个类型之关联起来,这种方式是ts中我们用的比较多的联合类型,我们在实际项目中具有多种情况值,可以通过联合类型的方式进行声明,例如string | number这样的;
2、createContext与useContext解决跨组件数据
接下来,我们需要接着上一次的代码,做一些改动调整,去实现一个老生常谈的todoList,具体界面如下:
我大概解释一下数据模型和通过reducer实现部分全局状态管理数据的办法,那么我们先看一下数据的处理办法,如下图所示:
具体我们可以用typescript中的interface对于我们的数据类型进行定义,同样我们可以参考下面示例的注释方式,这样我们就可以使用原子属性就会有一个非常良好的文字提示,注释写的好,妈妈就再也不用担心我迷路了~
下面是代码部分
export interface Task {
/**
* @description 任务id
*/
id: string
/**
* @description 任务名称
*/
text: string
}
export interface List {
/**
* @description 任务板块id
*/
id: string
/**
* @description 板块名称
*/
text: string
/**
* @description 当前板块任务list
*/
tasks: Task[]
}
export interface AppState {
lists: List[]
}
export interface AppStateContextProps {
lists: List[]
}
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: "车票已经作废了" }]
}
]
}
对于上面的todoList我们用这样的方式去创建数据模型,可以满足block + list的展现模式
接下来,我们通过React中提供的两个api(createContext, useContext)来去实现数据全局挂载了,如下图所示:
实际项目用法具体如下:
import React, { createContext, useContext } from 'react'
export interface Task {
/**
* @description 任务id
*/
id: string
/**
* @description 任务名称
*/
text: string
}
export interface List {
/**
* @description 任务板块id
*/
id: string
/**
* @description 板块名称
*/
text: string
/**
* @description 当前板块任务list
*/
tasks: Task[]
}
export interface AppState {
/**
* @description 板块list
*/
lists: List[]
}
export interface AppStateContextProps {
lists: List[]
getTasksByListId(id: string): Task[]
}
const appStateReducer = (state: AppState, action: any): AppState => state
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 getTasksByListId = (id: string) => {
return appData.lists.find((i: List) => i.id === id)?.tasks || []
}
return (
<AppStateContext.Provider value={{ ...appData, getTasksByListId }}>
{children}
</AppStateContext.Provider>
)
}
export const useAppState = () => {
return useContext(AppStateContext)
}
针对我们AppStateProvider,我们需要挂载到组件的最外层,这样我们就可以在组件内部去使用useContext返回的内容值,因为demo中有现在所有内容都卸载App.tsx上面,AppStateProvider就需要挂载在App.tsx外层,
具体如下:
import React from 'react'
// import Count from './components/Count';
import Todo from './pages/Todo'
import { AppStateProvider } from './pages/Todo/TodoStateContext'
import { AppContainer } from './styles'
const App: React.FC = () => {
return (
<AppContainer>
<AppStateProvider>
<Todo />
</AppStateProvider>
{/* useReducer模拟写法 */}
{/* <Count></Count> */}
</AppContainer>
);
}
export default App;
接下来就是我们在各个分散的组件中使用方式了,首先是Columns列中使用的办法
import { ColumnContainer, ColumnTitle } from "../../styles"
import Card from "./Card"
import { Task, useAppState } from "./TodoStateContext"
type ColumnProps = {
text: string
id: string
}
const Column: React.FC<ColumnProps> = ({ text, id }) => {
const { getTasksByListId } = 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}`} />
))
}
</ColumnContainer>
)
}
export default Column
通过上面一系列操作,我们用完成了组件可以使用当前AppStateProvider注入进来的数据,做一系列操作,勉强可以跨多个组件做一些公共数据的处理,下面我们将重点放在如何通过useReducer来去做数据的增删改查,本章到此,附上本章的代码tag地址,感兴趣本系列内容的兄弟姐妹们,点个关注~感谢各位看官转评赞一键三连我也不介意~~~~