useReducer
官网传送门
前言
const [state, dispatch] = useReducer(reducer, initialArg, init);
useState
的替代方案。它接收一个形如 (state, action) => newState
的 reducer
,并返回当前的 state
以及与其配套的 dispatch
方法。(如果你熟悉 Redux
的话,就已经知道它如何工作了。)
在某些场景下,useReducer
会比 useState
更适用,例如 state
逻辑较复杂且包含多个子值,或者下一个 state
依赖于之前的 state
等。并且,使用 useReducer
还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch
而不是回调函数 。
以下是用 reducer
重写 useState
一节的计数器示例:
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
reducer 的幂等性
reducer
本质是一个纯函数,没有任何UI和反作用。这意味着相同的输入(state、action
),reducer
函数不管执行多少遍始终会返回相同的输出(newState
)。所以经过reducer
函数很容易推测state
的变化,而且也更加容易单元测试。
state 理解
state
是当前应用状态对象,能够理解就是咱们熟知的React里面的state
。
不少时候state
可能会是一个复杂的JavaScript
对象,针对这种场景咱们可使用ES6
的结构赋值:
// 返回一个 newState (newObject)
function countReducer(state, action) {
switch(action.type) {
case 'add':
return { ...state, count: state.count + 1; }
case 'sub':
return { ...state, count: state.count - 1; }
default:
return count;
}
}
关于上面这段代码有两个重要的点须要咱们记住:
-
reducer
处理的state
对象必须是immutable
(不可变的),这意味着永远不要直接修改参数中的state
对象,reducer
函数应该每次都返回一个新的state object
- 既然
reducer
要求每次都返回一个新的对象,咱们可使用ES6
中的解构赋值方式去建立一个新对象,并复写咱们须要改变的state属性,如上例。
但若是咱们的state是多层嵌套,解构赋值实现就很是复杂:
function bookReducer(state, action) {
switch(action.type) {
// 添加一本书
case 'addBook':
return {
...state,
books: {
...state.books,
[bookId]: book,
}
};
case 'sub':
// ....
default:
return state;
}
}
对于这种复杂state
的场景推荐使用immer
等immutable
库解决。
action 理解
action
:用来表示触发的行为。
- 用
type
来表示具体的行为类型
- 用
payload
携带的数据
const action = {
type: 'addBook',
payload: {
book: {
bookId,
bookName,
author,
}
}
}
function bookReducer(state, action) {
switch(action.type) {
// 添加一本书
case 'addBook':
const { book } = action.payload;
return {
...state,
books: {
...state.books,
[book.bookId]: book,
}
};
case 'sub':
// ....
default:
return state;
}
}
总结
reducer
是一个利用action
提供的信息,将state
从preState
转换到newState
的一个纯函数,具备一下几个特色:
- 语法:
(state, action) => newState
- Immutable:每次都返回一个
newState
, 永远不要直接修改state对象
- Action:一个常规的
Action
对象一般有type
和payload
(可选)组成
- type: 本次操做的类型,也是
reducer
条件判断的依据
- payload: 提供操做附带的数据信息
useState + useContext 实现 redux 数据管理效果
在某些场景下,useReducer
会比 useState
更适用,例如 state
逻辑较复杂且包含多个子值,或者下一个 state
依赖于之前的 state
等。并且,使用 useReducer
还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch
而不是回调函数 。
import React,{useReducer,useContext} from 'react'
const initailState = {
a:"11111",
b:"11111"
}
const reducer = (prevState,action)=>{
let newstate = {...prevState}
switch(action.type){
case "change-a":
newstate.a = action.value
return newstate
case "change-b":
newstate.b = action.value
return newstate
default:
return prevState
}
// return prevState
}
const GlobalContext = React.createContext()
export default function App() {
const [state, dispatch] = useReducer(reducer, initailState)
return (
<GlobalContext.Provider value={
{
state,
dispatch
}
}>
<div>
<Child1/>
<Child2/>
<Child3/>
</div>
</GlobalContext.Provider>
)
}
function Child1(){
const {dispatch} = useContext(GlobalContext)
return <div style={{background:"red"}}>
<button onClick={()=>{
dispatch({
type:"change-a",
value:"2222222"
})
}}>改变a</button>
<button onClick={()=>{
dispatch({
type:"change-b",
value:"333333"
})
}}>改变b</button>
</div>
}
function Child2(){
const {state} = useContext(GlobalContext)
return <div style={{background:"yellow"}}>
child2-{state.a}
</div>
}
function Child3(){
const {state} = useContext(GlobalContext)
return <div style={{background:"gray"}}>
child3-{state.b}
</div>
}