前言
由于React的函数式组件使用起来方便(对比class组件),我将重点使用函数组件来运行开发。在这系列博客中,我将分享我所学到Hook系列API的知识。 Hooks系列主要分以下内容:
什么是useReducer
React 本身不提供状态管理功能,通常需要使用外部库。这方面最常用的库是 Redux。
Redux 的核心概念是,组件发出 action 与状态管理器通信。状态管理器收到 action 以后,使用 Reducer 函数算出新的state,Reducer 函数的形式是(state, action) => newState
useReducer
用来引入 Reducer 功能。
useReducer语法
const [state, dispatch] = useReducer(reducer, initialState);
useReducer用来接收两个参数,分别是Reducer函数和初始state
useReducer 返回了一个数组,2个元素分别为 state 和 dispatch 方法。其中 state 在我们的例子中就是当前的 n 值,dispatch 方法接受一个参数,执行对应的 action。dispatch 执行后,对应的 state 会改变,组件会 rerender,来展示最新的状态。
reducer函数
reducer函数里面可以存放state的各种操作,它类似状态管理器(其实我觉得应该叫state数据管理器),通过dispatch函数的传递action,可以触发reducer函数内部运算操作,并返回当前state
用法
首先,我们要定义一个Reducer函数,下面的函数将定义加法和减法
const reducer=(state,action)=>{
if(action.type==="add"){
return {
n:state.n+1
}
}else if(action.type==="sub"){
return {
n:state.n-1
}
}
}
其次,使用useReducer接收Reducer函数和一个初始state,并返回当前值state与dispatch函数
当触发事件时,使用dispatch传递action,让reducer计算出新的state
export default function App() {
const initialState = { n: 1 };
const [state, dispatch] = useReducer(reducer, initialState);
//使用useReducer接收reducer参数和初始state
return (
<>
<div>{state.n}</div>
<button
onClick={() => {
dispatch({ type: "add" }); // 传递action
}}
>
点击+
</button>
<button
onClick={() => {
dispatch({ type: "sub" });//传递action触发reducer函数
}}
>
点击-
</button>
</>
);
}
当传递的action的type是对应值时,就会算出对应的内容
dispatch()是发出 Action 的唯一方法。它发出action后使reducer执行,并返回一个新的state。
小结
我们学习到reducer的使用步骤
- 先定义一个initialValue
- 定义一个reducer函数,把所有操作方法都丢进去
- 把initialValue跟reducer通过useReducer关联起来,并返回一个当前state和dispatch
- 当需要计算时,使用dispatch传递一个action值,触发reducer函数执行,返回一个新的state
新的示例
来看一个比较复杂的示例
import React from "react";
import "./styles.css";
// 定义操作器
const reducer = (state, action) => {
if (action.type === "change") {
return { ...state, ...action.formData };
} else if (action.type === "reset") {
return { name: "", age: "", love: "" };
}
};
export default function App() {
const [state, dispatch] = React.useReducer(reducer, {
name: "",
age: "",
love: ""
});
const onclick = () => {
alert(`您的姓名是${state.name},年龄是${state.age},您喜欢${state.love}`);
};
return (
<>
<form>
<div>
姓名
<input
onInput={(e) => {
dispatch({ type: "change", formData: { name: e.target.value } });
}}
/>
</div>
<div>
年龄
<input
onInput={(e) => {
dispatch({ type: "change", formData: { age: e.target.value } });
}}
/>
</div>
<div>
喜欢
<input
onInput={(e) => {
dispatch({ type: "change", formData: { love: e.target.value } });
}}
/>
</div>
<button type="reset">重置</button>
<button type="button" onClick={onclick}>
确认
</button>
</form>
<hr />
</>
);
}
上面使用了action传递更多的属性来进行更多的计算可能性。代码直接直接复制到代码沙盒中运行。
使用useReducer替代redux
我们可以使用useContext配合useReducer来帮助我们完成一些数据、操作的流转
主要分成以下步骤
创建store用来存放数据
示例:
const store={
user:null,
age:null,
movies:null
}
Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。
创建reducer函数存放操作
示例:
const reducer = (state, action) => {
switch (action.type) {
case "setUser":
return { ...state, user: action.user };
case "setBooks":
return { ...state, age: action.age };
case "setMovies":
return { ...state, movies: action.movies };
default:
break;
}
}
reducer是一个操作管理器,当Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。
Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
创建一个Context
const Context =React.createContext()
创建上下文
创建读写api
const [state, dispatch] = useReducer(reducer, store);
store需要知道reducer函数,做法就是使用useReducer将store与reducer关联起来,再通过dispatch发出action
Context.provider提供Context
主要渲染组件
return (
<Context.Provider value={{state:state,dispatch:dispatch}}>
<User />
<hr />
<Books />
<Movies />
</Context.Provider>
);
在provider内的所有组件都可以通过useContext来获取到value里面的API
各个组件使用useContext获取读写api
子组件
const { state, dispatch } = useContext(Context);
表结构编程
上面的步骤已经介绍完毕,但是有时候数据多,reducer的代码量很大,那么能否将代码完善一下?
可以使用表结构编程,将reduce里面的代码保存在一个对象里。
const fnObj = {
setUser: (state, action) => {
return { ...state, user: action.user };
},
setBooks: (state, action) => {
return { ...state, books: action.books };
},
setMovies: (state, action) => {
return { ...state, movies: action.movies };
}
};
此时的reducer为
function reducer(state, action) {
const fn = obj[action.type];
if (fn) {
return fn(state, action);
} else {
throw new Error("这是不对的");
}
}
这种方法比较适合模块化开发,因为对象是可以合并的,所以当模块化后就可以通过...运算符将不同的逻辑运算的对象合并到一个表对象里面,类似于这样
const objFn={
...app,
...children
}
总结
我们学习到使用reducer函数的步骤以及通过useContext(上下文)provider(提供)让各组件共享state的操作步骤
如果需要优化reducer的代码,则推荐表结构编程来让代码更加优雅