React hooks 通过 context 和 useReducer 实现状态管理
状态管理 是 解决React 中组件层级较深而数据是自上而下流动,而造成的数据传递较为复杂,而且组件必须要一级一级传递,导致组件之间的耦合比较严重,有悖于 组件设计的原则。从而需要使用状态管理,来处理这些数据。
解决这个问题还有个 redux 可以使用,但是需要安装 redux 插件,通常使用 react-redux,但是目前 react 版本已经更新到了16.8 以后,hooks 已经流行,可以通过自身的 useReducer 解决 状态管理的问题,就是我们不必再去安装插件就可以处理 状态管理。也是为项目的体积进行减负。
1. useReducer
const [state, dispatch] = useReducer(reducer, state);
useReducer 是 React 自带的 hooks,它接收两个参数,reducer 和 state 的初始值,调用后会返回一个数组,数组第一项是 state,第二项是 dispatch 方法,用于修改 state ;
当组件中有比较复杂的状态需要管理,或者需要跨越很深的组件层级去更新状态的时候,useReducer 比 useState 更好的选择。
简单的 useReducer 示例
const initState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'add':
return { count: state.count + 1 };
case 'sub':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter({ initialState }) {
const [state, dispatch] = useReducer(reducer, initState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: 'add' })}>+</button>
<button onClick={() => dispatch({ type: 'sub' })}>-</button>
</>
);
}
上面的 useReducer 示例虽然代码简洁,但是 包含了 redux 的核心部分,通过 dispatch 派发 action 去修改数据,最后返回给页面新数据。当然这里还是不能解决跨越多层级传递 dispatch 方法,需要使用 context。
2. Context
useContext 是一种无需通过传递 props 而可以直接在组件树中传递数据的技术。它是通过创建 provider 组件使用,通常还会创建一个 Hook 以在子组件中使用该值。
通过 React.createContext 方法生成一个 Context 对象,这个对象包含两个属性 Provider 提供器 和 Consumer 接收器
Provider 接受一个 value,该属性会被 provider 提供给后代组件,通过 Consumer 接收
// 当你没有一个合理的默认值时,这种也是有效的,而在这些情况下,null 作为默认值可能感觉是合理的。
// 但是,为了让类型系统理解你的代码,你需要在 createContext 上显示设置 value | null.
const MyContext = createContext(null)
// value 可以防止对象,也可以防止单个值,也可以放数组等等任意值
<MyContext.Provider value={value}>
Consumer 使用 prop 为子组件提供 value 属性,这个 value 就是 provider 中定义好的
<MyContext.Consumer>
{value => /* 根据 context value 渲染一些内容 */}
</MyContext.Consumer>
3. 实现状态管理 store
状态管理需要 使用 useReducer 和 context 相结合来实现。
示例:创建 store
store/StoreContext.js 创建 context 仓库用于存放数据
/*
* @Description: 创建仓库,主要是使用该仓库 提供数据,使用 provider
**/
import {createContext} from 'react';
const StoreContext = createContext();
export default StoreContext;
store/MyProvider.js 创建 共享状态的提供器
/*
* @Description: 提供器组件封装,给子组件提供数据 provider
**/
import React, {useReducer} from 'react';
import MyStore from './StoreContext';
import reducer from './reducer';
const MyProvider = ({children}) => {
const [reducerState, dispatch] = useReducer(reducer, {
userInfo: {} // 个人信息
});
return (
<MyStore.Provider value={{reducerState, dispatch}}>
{children}
</MyStore.Provider>
);
};
export default MyProvider;
store/useStore.js 自定义函数,目的是方便获取 reducer 中的 state 和 dispatch
/*
* @Description: 方便子组件获取 提供器中提供的数据
**/
import {useContext} from 'react';
import StoreContext from './StoreContext';
// 使用 use 开头,便于 React 能识别这是一个自定义 Hook
const useStore = () => {
const [state, dispatch] = useContext(StoreContext);
return {state, dispatch};
};
export default useStore;
store/reducer.js reducer 创建管理员,管理数据
/*
* @Description: reducer 管理数据
**/
import {SET_USERINFO} from './actionType';
const reducer = (state, action) => {
switch (action.type) {
case SET_USERINFO:
return {...state, ...{userInfo: action.userInfo}};
default:
return state;
}
};
export default reducer;
store/actionType.js 创建 type 管理文件
/*
* @Description: 统一管理type,利于维护
**/
export const SET_USERINFO = 'setUserinfo';
在组件中使用
import React from 'react';
import { Provider } from './store';
import Count from './Count';
const initialState = 0;
const Test = ()=> {
return (
<Provider initialState={initialState}>
<Count />
</Provider>
);
}
export default Test;
在子组件中使用只要通过一行代码就可获取 state 和 dispatch ,获取数据 和 进行修改数据
const { state, dispatch } = useStore();
以上就封装好了 store 用于管理数据。和 redux 只有单一的状态树不同,我们可以在组件树中多次插入 Provider 。调用 useStore 时会向上查找,并自动获取离当前组件最近的 provider 中保存的状态。
这里唯一的坏处就是不能使用 redux 中间件,redux-thunk 和 redux-sage 解决异步修改数据的问题。
转载自:https://juejin.cn/post/7378634249547972671