useContext与useReducer结合使用达到react-redux的效果

4,986 阅读3分钟

useContext与useReducer结合使用达到react-redux的效果

userContext

传统的context使用是一件比较麻烦的事情,而且代码可读性比较差。使用useContext只需要一下几个步骤

  1. 使用React.createContext创建context对象
  2. 创建顶层容器组件
  3. 将state、setState传递给子组件
  4. 子组件使用useContext获取state、setState,达到获取值修改值的目的
import React, {useState, useContext} from 'react'
// 1.创建context对象
const Context = React.createContext()

// 2. 顶层容器组件创建

export function ContentHook(props) {
  
  // 3.各种state、setState
  const [name, setName] = useState('jgmiu')
  const [age, setAge] = useState('24')
    
  // 通过Context.Provider将值与改变值的方法传递下去
  return (
    <Context.Provider value = {{name, setName, age, setAge}}>
      <h3>useContent</h3>
      <Child1 />
      <Child2 />
    </Context.Provider>
  )
}

function Child1(props) {
  // 得到context对象 获取里面的值用于展示
  const user = useContext(Context)
  return(
    <div className={'c1'}>
      <h3>子组件1</h3>
      <span>name:{user.name} age:{user.age} </span>
    </div>
  )
}

function Child2(props) {
  const user = useContext(Context)
    
  // 得到set方法    
  const handleName = e =>{
    user.setName('缪佳耕')
  }

  const handleAge = e => {
    user.setAge(30)
  }

  return(
    <div className={'c2'}>
      <h3>子组件2</h3>
      <button onClick={handleName}>name</button>
      <button onClick={handleAge}>age</button>
    </div>
  )
}

子组件2改变context对象里面的值,子组件1也会跟着变

userReducer

userReducer接受两个参数,第一个参数是reducer,第二个参数是store,返回state与dispatch

// 1. store 初始值
const store = {
   user: {name: 'jgmiu', age: 24},
}

// 2. reducer
const user = (state, action) => {
  switch (action.type) {
    case 'name':
      return {...state, name: action.value}
    case 'age':
      return {...state, age: action.value}
    default:
      return state  
  }
}

// 使用useReducer得到 state 与dispatch
const [state, dispatch] = useReducer(user, store)

在实际开发中,可能有很多歌reducer。在以前使用react-redux的使用提供一个方法combineReducers,用于合并多个reducer。经过测试这里使用这个方法会报错所以需要自己去封装一个

// 自定义合并reducer函数
const combineReducers = (reducers) => {
  return function(state, action) {
    return Object.keys(reducers)
                 .map(k => ({[k]: reducers[k](state[k], action)}))
                 .reduce((prev, cur) =>(Object.assign({}, prev, cur)))
  }
}

以上代码来着:scarletsky.github.io/2016/08/20/…

两者结合

两者结合使用能够达到react-redux的效果。其主要思路如下:

  • 使用userReducer得到state与dispatch
  • 使用userContext将state与dispatch共享到子组件
import React, { useReducer,useContext } from 'react'

// 创建store
const store = {
   user: {name: 'jgmiu', age: 24},
   params: {key: 'key', date: '20190802'}
}

// reducer 创建
const user = (state, action) => {
  switch (action.type) {
    case 'name':
      return {...state, name: action.value}
    case 'age':
      return {...state, age: action.value}
    default:
      return state  
  }
}
const params = (state, action) => {
  switch (action.type) {
    case 'key':
      return {...state, key: action.value}
    case 'date':
      return {...state, date: action.value}  
    default: 
      return state
  }
}

// 自定义合并reducer函数
const combineReducers = (reducers) => {
  return function(state, action) {
    return Object.keys(reducers)
                 .map(k => ({[k]: reducers[k](state[k], action)}))
                 .reduce((prev, cur) =>(Object.assign({}, prev, cur)))
  }
}
const reducers = combineReducers({user, params})

// 创建Context放在最上层父组件
const Context = React.createContext()

export default function Redux2(props) {
  // 在最顶层得到 store 与 dispatch
  const [state, dispatch] = useReducer(reducers, store)

  return (
    <Context.Provider value = {{state, dispatch}}>
      <h1>使用useContext/useReducer代替reducer</h1>
      <Child1 />
      <Child2 />
    </Context.Provider>
  )
}

// 子组件1
function Child1(props) {
  const context = useContext(Context)
  const user = context.state.user
  const params = context.state.params

  return(
    <div className={'c1'}>
      <h3>子组件1</h3>
      <p><span>name: {user.name} age: {user.age}</span></p>
      <p><span>key: {params.key} date: {params.date}</span></p>
    </div>
  )
}

// 子组件2
function Child2(props) {
  const context = useContext(Context)
  const handleName = e =>{
    context.dispatch({type: 'name', value: 'miujg'})
  }

  const handleAge = e => {
    context.dispatch({type: 'age', value: 30})
  }

  const handleKey = e => {
    context.dispatch({type: 'key', value: 'keykeykey'})
  }

  const handleDate = e=> {
    context.dispatch({type: 'date', value: '1212121212'})
  }

  return(
    <div className={'c2'}>
      <h3>子组件2</h3>
      <button onClick={handleName}>name</button>
      <button onClick={handleAge}>age</button>
      <button onClick={handleKey}>key</button>
      <button onClick={handleDate}>date</button>
    </div>
  )
}

简单demo的完整代码

小结

网上也有很多例子,很多标题都是说使用hook来代替传统的redux。能不能代替?小项目能代替?大项目也能代替?这些问题还得多实践才知道~~~~~