新项目开发整理(三)使用Context实现redux

1,196 阅读3分钟

前言

在一些中小型的项目中,我们也不可避免的会有需要做状态管理的需求,但是引入redux好像又有点杀鸡焉用牛刀的感觉,所以这种情况下我们就可以用reactContext来手动实现一个简单的redux(使用react hooks)

什么是Context

react的数据流是一个自顶向下的单向数据流,通过props逐级往下传递数据,当层级变得复杂的时候,传递props就会变得极其繁琐,而react则给出了一种不需要手动添加props就可以在组件间共享数据的能力,这就是Context

一、创建一个Context对象

先在入口文件里创建一个Context对象,然后我们需要把这个Context对象套在最外层组件的外面,并且要给他一些默认值,这里我们使用react hooks提供的useReducer来生成默认值。

//index.js
export const IndexContext = createContext();
const Main = () => {
  const [state, dispatch] = useReducer(reducers, init)
  return (
    <IndexContext.Provider value={{state, dispatch}}>
      <Router></Router>
    </IndexContext.Provider>
  )
}

这里的reducers是项目中用到的所有的reducer方法的集合,init则是Context的默认值,这里提到的reducer与我们在redux中使用的reducer概念是相同的,我们把我们的reducer和默认值作为参数传进useReducer函数会得到一个返回数组,数组第一项是Context对象的值,第二项为触发reducerdispatch函数。另外我们平时写redux时会按组件对reducer进行文件区分,最终通过combinReducer再合为一个进行导出,这样方便开发,同样我们也可以自己实现一个combinReducer

二、combinReducer与reducer文件结构

//userReducer.js
const init = {
  userId: "",
  token: "",
  userName: "",
  phone: "",
  openid: ""
};
export const userReducer = (state = init, action) => {
  switch (action.type) {
    case "login": {
      return {
        ...state,
        ...action.payload
      };
    }
    case "logout": {
      return {
        ...state,
        userId: "",
        token: "",
        userName: "",
        phone: "",
        openid: ""
      };
    }
    default:
      return state;
  }
};
//这是某一个reducer的文件,要实现combinReducer实际上就是把所有文件的init对象合并为一个对象,某一个action产生时遍历触发所有文件中对该action的reducer,实现如下

//index.js
const combinReducer = reducer => {
  const reducerKeys = Object.keys(reducer)
  let initStateObj = {}
  reducerKeys.forEach(key=>{
    const initState = reducer[key](undefined, {type: ""})
    if(!initState){
      throw new Error(`${key}没有返回state`)
    }else{
      initStateObj[key] = initState
    }
  })

  return (state, action) => {
    if(action){
      reducerKeys.forEach(key=>{
        const prevState = initStateObj[key]
        initStateObj[key] = reducer[key](prevState, action)
      })
    }
    return {...initStateObj}
  }
}
export const reducers =  combinReducer({userReducer, chooseReducer, myReducer})

三、在组件中使用

有了以上步骤之后,我们在组件中通过useContext就可以拿到订阅的值和dispatch方法

// *.jsx
const { state, dispatch } = useContext(IndexContext);
const XX = () => {
    useEffect(()=>{
        request("xxxx").then(data => {
            dispatch({
                type: "setData",
                data
            })
        })   
    })
    return (
        <Fragment>
            state.xxreducer.list.map(item=>{
                return <div></div>
            })
        </Fragment>
    )
}
//在业务组件中诸如此类使用即可

后记

可以看到使用react hooks实现的状态管理,在语法上使用习惯上十分接近redux,同时又十分灵活小巧,比较适合中小型项目,没有那么多状态需要管理的情况,像我们简单的用户登录状态的全局保持,或者是列表页返回需要停在原来位置,不重新刷新页面之类的简单需求,就不用再引入整个redux,自己动手实现即可。