使用useContext和 useReducer来实现全局状态管理

3,576 阅读2分钟

hook的出现使得函数组件中有了自己的状态可以维护,但是组件之间的传参还是有点复杂,这里我们使用useContext和useReducer这两个钩子来实现组件之前的通信,并且可以管理全局状态

目录结构

提供全局数据(context.js)

给provider注入的value属性包括

  • state: 全局状态
  • dispatch: 事件触发器
  • getValue: 根据key获取state的函数
  • setValue: 根据key设置state的函数
import React, { useReducer } from 'react';
import {initState, reducer} from './reducer';

export const Context = React.createContext({});

export function ConfigContext({children}) {

  const [state, dispatch] = useReducer(reducer, initState);
  const getValue = (key) => state[key];
  const setValue = (key, value ) => {
    return dispatch({type: 'SET_VALUE', key, value });
  }

  let ctx = {
    state,
    dispatch,
    getValue,
    setValue,
  };
  return <Context.Provider value={ctx} >{children}</Context.Provider>
}

状态处理函数(reducer.js)

提供一个初始状态,并返回最新的状态;

// 初始状态
export const initState = {
  count: 0,
};

export function reducer(state,{type, ...payload}){
  switch(type) {
    case 'SET_VALUE':{
      return {
        ...state,
        [payload.key]:[payload.value],
      };
    }
    default: {
      return state;
    }
  }
}

组件使用(设置状态值)

import React, { Fragment, useContext } from 'react';
import {Context} from '../context';

export default function Count() {
  const {state:{count}, setValue} = useContext(Context);
  return (
    <Fragment>
      <button onClick={() => setValue("count", +count+1)}> + 1 </button>
      <button onClick={() => setValue("count", +count-1)}> - 1 </button>
    </Fragment>
  );
}

组件使用(获取状态值)

import React, { Fragment, useContext } from 'react';
import {Context} from '../context';

export default function Show() {
  const {state:{count}} = useContext(Context);
  return (
    <Fragment>
      <div>{`现在的数字是${count}`}</div>
    </Fragment>
  );
}

index.js

import React from 'react';
import {ConfigContext} from './context';
import Count from './components/count';
import Show from './components/show';

export default function Page() {
  return <ConfigContext>
    <Count/>
    <Show/>
  </ConfigContext>
}

演示

注意事项

到这里就可以实现全局状态的管控了,但是需要注意的是,只要引用了Context的组件,不管你依赖的数据是否发生变化,只要state发生变化,这个组件就会从新渲染,如果数据量比较大的话,渲染性能比较差。优化的话可以使用memo等方法进行优化;

优化前

import React, { Fragment, useContext } from 'react';
import {Context} from '../context';

export default function Test() {
  const {state:{id}} = useContext(Context);
  return (
    <Fragment>
      <div>我的id没有发生变化{id}</div>
    </Fragment>
  );
}

可以看到id并没有变化,但是还是会重新渲染这个组件。

优化后

import React, { Fragment, useContext } from 'react';
import {Context} from '../context';

const Sub = React.memo(props => {
  console.log('我被渲染了', props)
  return (
    <div>现在的id是{props.id}</div>
  );
});

export default function Test() {
  const {state:{id}} = useContext(Context);
  return (
    <Sub id={id}/>
  );
}