useReducer用最简单的话来说,就是允许我们在函数组件里面像使用redux一样通过reducer和action来管理我们组件状态的变换。
我们可以通过useContext配合实现一个函数组件中的redux。
useReducer的标准写法:
const [state, dispatch]=useReducer(reducer, initialArg, init?)
参数解析:
state 通过解构得到当前组件的状态;
dispatch 是用来触发某些改变state的action而不是直接设置state的值,至于不同的action如何产生新的state的值则在reducer里面定义;
reducer 是一个函数,形式为(oldstate,action)=>newstate,两个形参一个接收当前的state、一个接收当前dispatch的action为参数;
initialArg 如果调用者没有提供第三个init参数,这个参数代表的是这个reducer的初始状态,如果init参数有被指定的话,initialArg会被作为参数传进init函数来生成初始状态;
init(可选)这是一个用来生成初始状态的函数,initialArg 作为参数传入,函数的返回值作为初始状态。
写一个例子演示一下使用
import React,{useReducer} from 'react'
export default function App() {
//定义reducer函数
let reducer=(initSate,action)=>{
if(action.type=="MSG"){
initSate.msg=action.value //把当前状态的msg修改为ation接收到的对象的msg
}
initSate=JSON.parse(JSON.stringify(initSate))//对象深拷贝,形成一个新的对象
return initSate//返回新的状态对象,作为组件的状态
}
//使用useReducer,定义初始状态为{msg:"hello"}
let [state,dispatch]=useReducer(reducer,{msg:"hello"})
return (
<div>
//使用数据与useState创建的状态用法相同
<p>{state.msg}</p>
//调用dispatch修改状态,render的action会接收传入的对象
<button onClick={()=>{dispatch({type:"MSG",value:"修改了msg数据"})}}修改仓库msg</button>
</div>
)
}
useReducer和useState
两者的用法有点相似,都是使用hook初始化状态,然后得到的值通过解构得到当前状态和修改状态的方法。
区别:
useReducer将状态和状态的变化统一管理在reducer函数里面,这样对于一些复杂的状态管理会十分方便我们debug,因为它对状态的改变是封闭的。而由于useState返回的setState可以直接在任意地方设置我们状态的值,当我们组件的状态转换逻辑十分复杂时,它将很难debug,因为它是开放的状态管理。
难度进阶——通过useReducer、useContext、context实现一个Redux插件的功能
1、首先创建一个上下文对象
import React from 'react'
let ctx=React.createContext(null)
export default ctx;
2、将上下文引入我们的仓库组件,设置初始状态和设计reducer的处理逻辑,然后提供状态和dispatch修改函数供全局使用。
import React,{useReducer} from 'react'
import ctx from "./store"
export default function Myredux(props) {
let reducer=(state,action)=>{
if(action.type=="MSG"){
state.msg=action.value
}
if(action.type=="TOKEN"){
state.token=action.value
}
state=JSON.parse(JSON.stringify(state))
return state
}
let [store,dispatch]= useReducer(reducer,{msg:"hello",token:"af123123"},function(arg){
//可以处理数据 然后返回值作为仓库的初始状态
arg.msg="123"
return arg
})
return (
<ctx.Provider value={[store,dispatch]}>
{props.children}
</ctx.Provider>
)
}
3、最后一步,此时我们提供的数据还不能被其它组件使用,我们需要将我们的仓库组件作为根组件,这样所有组件才能访问到达到全局使用。
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from "./App.jsx"
import Myredux from './store/index';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Myredux><App></App></Myredux>);
在其它组件中使用时,我们只需引入上下文,然后通过useContext获取提供的数据即可
import React,{useContext} from 'react'
import ctx from "./store/store"
import Box from './Box.jsx'
export default function App() {
let [store,dispatch]=useContext(ctx)
return (
<div>
<h1>App</h1>
<p>App--{store.msg}</p>
<button onClick={()=>{dispatch({type:'MSG',value:"666app修改了仓库"})}}>change</button>
</div>
)
}
到此重要的hook就差不多学完了。