likes
comments
collection
share

React hooks 通过 context 和 useReducer 实现状态管理

作者站长头像
站长
· 阅读数 72

状态管理 是 解决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
评论
请登录