简介
useReducer是React提供的一个高级Hook,它不像useEffect、useState、useRef等必须hook一样,没有它我们也可以正常完成需求的开发。
简单来说 reducer是一个函数(state, action) => newState:接收当前应用的state和触发的动作action,计算并返回最新的state
例子
常规
import {useCallback, useState} from "react";
export const useUndo = <T>(initialPresent:T) =>{
const [state,setState] = useState<{
past:T[],
present:T,
future:T[]
}>({
past:[],
present:initialPresent,
future:[]
})
const canUndo = state.past.length !== 0;
const canRedo = state.future.length !== 0;
const undo = useCallback(() =>{
setState(currentState => {
const {past,present,future} = currentState;
if (past.length === 0) return currentState;
const previous = past[past.length - 1];
const newPast = past.slice(0,past.length - 1);
return {
past:newPast,
present:previous,
future:[present,...future]
}
})
},[])
const redo = useCallback(() => {
setState(currentState => {
const {past,present,future} = currentState;
if (future.length === 0) return currentState;
const next = future[0];
const newFurture = future.slice(1);
return {
past:[...past,present],
present:next,
future:newFurture
}
})
},[])
const set = useCallback((newPresent: T) =>{
setState(currentState => {
const {past,present} = currentState;
if (newPresent === present) return currentState;
return{
past:[...past,present],
present:newPresent,
future:[]
}
})
},[])
const reset = useCallback((newPresent:T) =>{
setState(currentState => {
return {
past:[],
present:newPresent,
future:[]
}
})
},[])
return [
state,
{set,reset,undo,redo,canUndo,canRedo}
] as const
}
使用useReducer
import {useCallback, useReducer, useState} from "react";
const UNDO = "UNDO";
const REDO = "REDO";
const SET = "SET";
const RESET = "RESET";
type State<T> = {
past:T[],
present:T,
future:T[]
}
type Action<T> = {
newPresent?:T,
type:typeof UNDO |typeof REDO|typeof SET | typeof RESET
}
const undoReducer = <T>(state:State<T>,action:Action<T>) =>{
const {past,present,future} = state
const {type,newPresent} = action
switch (action.type){
case "UNDO":
if (past.length === 0) return state;
const previous = past[past.length - 1];
const newPast = past.slice(0,past.length - 1);
return {
past:newPast,
present:previous,
future:[present,...future]
}
case "REDO":
if (future.length === 0) return state;
const next = future[0];
const newFurture = future.slice(1);
return {
past:[...past,present],
present:next,
future:newFurture
}
case "SET":
if (newPresent === present) return state;
return{
past:[...past,present],
present:newPresent,
future:[]
}
case "RESET":
return {
past:[],
present:newPresent,
future:[]
}
}
return state
}
export const useUndo = <T>(initialPresent:T) =>{
const [state,dispatch] = useReducer(undoReducer,{
past:[],
present:initialPresent,
future:[]
} as State<T>)
const undo = useCallback(() =>{dispatch({type:UNDO})},[])
const redo = useCallback(() => {dispatch({type:REDO})},[])
const set = useCallback((newPresent: T) =>{dispatch({type:SET})},[])
const reset = useCallback((newPresent:T) =>{dispatch({type:RESET})},[])
return [
state,
{set,reset,undo,redo}
] as const
}