1、什么是reducer?
reducer是一个函数(state, action) => newState
:接收当前应用的state和触发的动作action,计算并返回最新的state
2、useReducer
const [state, dispatch] = useReducer(reducer, initState, init);
useReducer
接收三个参数:
第一个参数:reducer。
第二个参数:初始化的state。如果没有第三个参数,则state默认是传入的值,返回值为最新的state和dispatch函数(用来触发reducer函数,计算对应的state)。
第三个参数:如果有这个参数,则对初始值state做一个更新(可以没有)
按照官方的说法:对于复杂的state操作逻辑,嵌套的state的对象,推荐使用useReducer。
3、案例
3.1 利用useReducer
改写加减操作
原例:
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>you click {count} times</p>
<button onClick={() => setCount(count + 1)}>click me to +</button>
<button onClick={() => setCount(count - 1)}>click me to -</button>
</div>
);
}
改写后
import React, { useReducer } from "react";
function reducer(state, action) {
switch (action.type) {
case "reset":
return { count: action.payload };
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
return state;
}
}
function init(initialCountState) {
return { count: initialCountState.count + 1 };
}
function Counter({ initialCount }) {
const [state, dispatch] = useReducer(
reducer,
initialCount, //通过参数传递来的默认值,如果没有第三个参数,则state默认是1
init //如果有这个参数,则对初始值state做一个更新
);
return (
<div>
Count:{state.count}
<button
onClick={() => dispatch({ type: "reset", payload: initialCount.count })}
>
reset
</button>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
</div>
);
}
function Example() {
return <Counter initialCount={{ count: 1 }} />;
}
export default class App extends React.Component {
render() {
return (
<div>
<Example />
<hr />
</div>
);
}
}
发现更难了。。原因很简单,因为加减这种逻辑很简单,没有必要使用useReducer
3.2 登录操作
利用useState
方法编写
import React, { useState } from "react";
function Example() {
const [name, setName] = useState(""); // 用户名
const [pwd, setPwd] = useState(""); // 密码
const [isLoading, setIsLoading] = useState(false); // 是否展示loading,发送请求中
const [error, setError] = useState(""); // 错误信息
const [isLoggedIn, setIsLoggedIn] = useState(false); // 是否登录
const login = (name, pwd) => {
setIsLoading(true);
if (name === "123" && pwd === "123") {
setError("");
setIsLoggedIn(true);
setIsLoading(false);
} else {
setError("error");
setName("");
setPwd("");
setIsLoggedIn(false);
setIsLoading(false);
}
};
return (
// 返回页面JSX Element
<div>
用户名:
<input
type="text"
onChange={(e) => setName(e.target.value)}
value={name}
/>
<br />
密码:
<input type="text" onChange={(e) => setPwd(e.target.value)} value={pwd} />
<br />
<button onClick={() => login(name, pwd)} disabled={isLoading}>
登录
</button>
<br />
{isLoggedIn ? "登录成功" : error}
</div>
);
}
export default class App extends React.Component {
render() {
return (
<div>
<Example />
<hr />
</div>
);
}
}
上面Demo我们定义了5个state来描述页面的状态,在login函数中当登录成功、失败时进行了一系列复杂的state设置。可以想象随着需求越来越复杂更多的state加入到页面,更多的setState分散在各处,很容易设置错误或者遗漏,维护这样的老代码更是一个噩梦。
利用useReducer
编写
import React, { useReducer } from "react";
const initState = {
name: "",
pwd: "",
isLoading: false,
error: "",
isLoggedIn: false,
};
function loginReducer(state, action) {
switch (action.type) {
case "name":
return {
...state,
name: action.payload.name,
};
case "pwd":
return {
...state,
pwd: action.payload.pwd,
};
case "login":
return {
...state,
isLoading: true,
error: "",
};
case "success":
return {
...state,
isLoggedIn: true,
isLoading: false,
error: "",
};
case "error":
return {
...state,
error: action.payload.error,
name: "",
pwd: "",
isLoading: false,
isLoggedIn: false,
};
default:
return state;
}
}
function Example() {
const [state, dispatch] = useReducer(loginReducer, initState);
const { name, pwd, isLoading, error, isLoggedIn } = state;
const login = (name, pwd) => {
dispatch({ type: "login" });
if (name === "123" && pwd === "123") {
dispatch({ type: "success" });
} else {
dispatch({
type: "error",
payload: { error: "登录失败" },
});
}
};
return (
<div>
用户名:
<input
type="text"
onChange={(e) => {
dispatch({
type: "name",
payload: { name: e.target.value },
});
}}
value={name}
/>
<br />
密码:
<input
type="text"
onChange={(e) => {
dispatch({
type: "pwd",
payload: { pwd: e.target.value },
});
}}
value={pwd}
/>
<br />
<button onClick={() => login(name, pwd)} disabled={isLoading}>
登录
</button>
<br />
{isLoggedIn ? "登录成功" : error}
</div>
);
}
export default class App extends React.Component {
render() {
return (
<div>
<Example />
<hr />
</div>
);
}
}
代码着实还是变长了,但也有好处
1、更好的可读性,我们也能更清晰的了解state的变化逻辑。
2、所有的state处理都集中到了一起,使得我们对state的变化更有掌控力,同时也更容易复用state逻辑变化代码
useReducer可以让我们将what
和how
分开。比如点击了登录按钮,我们要做的就是发起登陆操作dispatch({ type: 'login' })
,点击退出按钮就发起退出操作dispatch({ type: 'logout' })
,所有和how
相关的代码都在reducer中维护,组件中只需要思考What
,让我们的代码可以像用户的行为一样,更加清晰。