引言
React的v16.8新推出了钩子hook特性,让前端小朋友们能够更加愉快的使用函数式编程来编写你的组件。当然了,如果你更喜欢面向对象的编程方式,也可以仍然使用Class。本篇文章手把手教大家做一个小demo,用useReducer来撸一个伪redux。
建议朋友们先熟悉一下useState,useEffect和useReducer的原理,这里是门钥匙。
正题
熟悉react的同学都知道,redux是react生态下的状态管理器,用来管理组件之间的状态共享,而不需要一层一层的传递props。
redux没有使用什么黑魔法,如果你之前使用过,理解起本篇来简直so easy。之前没有接触过的小伙伴,这里再给个门钥匙。
话不多说,咱们先看看如何使用吧。
首先,useReducer接收两个参数,第一个参数是reducer处理函数,第二个是初始的state对象。返回一个是最新的state对象,第二个是dispatch函数,这个dispatch用来触发reducer,我们接下来会介绍到。
const [state, dispatch] = useReducer(likeReducer, initState)
我们来看看我们要传入的两个参数具体是怎么写的。
首先是初始化的state,是一个对象:
const initState = {
like: 0, // 点赞次数
jet: 0 // 刷火箭次数
}
然后是reducer函数,接收state和action,其中state是旧的state,第一次useReducer会将初始值initState传递过来;action包含type和payload。
// 你也可以将action像redux那样单独提取出来 也可以不提 这里为了方便 没有提取
// 定义了一个点赞reducer
function likeReducer (state: any, action: any) {
switch (action.type) {
case 'addLike': // 添加赞
return {
...state,
like: state.like + 1
}
case 'addJet': //刷火箭
return {
...state,
jet: state.jet + action.payload
}
case 'desLike': // 减少赞
return {
...state,
like: state.like - 1
}
default:
return state
}
}
看到这里,你就知道了,useReducer做的,就是将初始的state传递给reducer,并且返回state和dispatch函数。
每次触发dispatch之后,他都会更新新的state返回。
这里需要注意的是,我们使用了es6的解构符来创建新的state,朋友们不要直接修改原state,即使使用Object.assign()的时候也记得把第一个设置成{},因为如果比较发现state没有更新,系统不会触发相关组件的重新渲染。
现在可以试试使用了
const Button: React.FC= () => {
const [state, dispatch] = useReducer(likeReducer, initState)
const addClick = () => {
dispatch({type: 'addLike'})
}
const desClick = () => {
dispatch({type: 'desLike'})
}
return (
<div>
<button onClick={() => {addClick()}}>加赞:{state.like}</button>
<button onClick={() => {desClick()}}>减赞:{state.like}</button>
<button onClick={() => {dispatch({type: 'addJet',payload: 3})}}>刷3支火箭:{state.jet}</button>
</div>
)
}
你在想,这不就是把组件内的useState改成useReducer嘛?说好的父子组件间共享呢?别急,共享状态涉及到作用域,我们来引入一个作用域。
import { useContext } from "react";
然后在父组件的文件里,创建这个作用域。
interface IContextProps {
state: {
like: number;
jet: number
};
dispatch: ({type}:{type:string}) => void;
}
const LikeContext = React.createContext({} as IContextProps)
我们把Button作为父组件,改造一下:
const Button: React.FC= () => {
const [state, dispatch] = useReducer(likeReducer, initState)
const addClick = () => {
dispatch({type: 'addLike'})
}
const desClick = () => {
dispatch({type: 'desLike'})
}
return (
<LikeContext.Provider value={{state, dispatch}}> // 加入作用域提供者,将我们的state和dispatch都传递给子组件
<button onClick={() => {addClick()}}>加赞:{state.like}</button>
<button onClick={() => {desClick()}}>减赞:{state.like}</button>
<button onClick={() => {dispatch({type: 'addJet',payload: 3})}}>刷3支火箭:{state.jet}</button>
<FontUi /> // 子组件
</LikeContext.Provider>
)
}
再写一下子组件:
const FontUi: React.FC = (props) => {
const {state, dispatch} = useContext(LikeContext) // 这里就用useContext拿到了提供者提供的值 注意 子组件或孙组件需要包含在提供者里
return <button onClick={() => {dispatch({type: 'addLike'})}}>子组件dispatch{state.like}</button> // 可以调用dispatch并实时改变作用域内的state
}
就可以试试我们的伪redux啦!这里演示了父子组件的状态共享及修改,你自己也可以用孙组件尝试一下,放出demo全部码
import React, { useEffect, useContext, useReducer } from "react";
const initState = {
like: 0,
jet: 0
}
interface IContextProps {
state: {
like: number;
jet: number
};
dispatch: ({type}:{type:string}) => void;
}
const LikeContext = React.createContext({} as IContextProps)
function likeReducer (state: any, action: any) {
switch (action.type) {
case 'addLike':
return {
...state,
like: state.like + 1
}
case 'addJet':
return {
...state,
jet: state.jet + action.payload
}
case 'desLike':
return {
...state,
like: state.like - 1
}
default:
return state
}
}
const Button: React.FC= () => {
const [state, dispatch] = useReducer(likeReducer, initState)
const addClick = () => {
dispatch({type: 'addLike'})
}
const desClick = () => {
dispatch({type: 'desLike'})
}
return (
<LikeContext.Provider value={{state, dispatch}}>
<button onClick={() => {addClick()}}>加赞:{state.like}</button>
<button onClick={() => {desClick()}}>减赞:{state.like}</button>
<button onClick={() => {dispatch({type: 'addJet',payload: 3})}}>刷3支火箭:{state.jet}</button>
<FontUi />
</LikeContext.Provider>
)
}
const FontUi: React.FC = (props) => {
const {state, dispatch} = useContext(LikeContext)
return <button onClick={() => {dispatch({type: 'addLike'})}}>子组件dispatch{state.like}</button>
}
export default Button
文章写的不易,转载请注明出处。转载回传传送门