使用 hooks 和 connect 访问同一个 store

React Hooks 距离正式发布已经过去好几个月了,redux,mobx,也都支持了 Hooks 的用法,那么有没有可能用 React Context API & Hooks 来实现一个同时支持 class component 和 functional component 访问的 store 呢?答案是肯定的。

既然我们是基于 Context Api,那么先来创建一个 context 对象

// store.js
import React from 'react'

const initialStore = {}
const StoreContext = React.createContext(initialStore)
 

接着我们来构造两种访问 store 的方法:

Hooks 的方式:

// store.js
import {useReducer, useContext} from 'react'

// 声明 reducer
export const reducer = (state, action) => {
  switch (action.type) {
    case 'set':
      return { ...state, ...action.payload }
    case 'reset':
      return {}
    default:
      throw new Error(
        `action: '${action.type}' not defined`
      )
  }
}

// 基于原生 Hooks 实现
export const useStore = () => {
  return useReducer(reducer, useContext(StoreContext))
}

HOC 的方式:
HOC 需要有一个上下文环境才可以访问 store,所以我们先来构造 provider

// 构造一个根组件 Provider
export const StoreProvider = ({ children }) => {
  const [context, dispatch] = useStore()
  return <StoreContext.Provider value={{ ...context, dispatch }}>{children}</StoreContext.Provider>
}

这个 StoreProvider 可以像 react-redux 的 Provider 一样,包裹在根组件的最外层

然后来看 consumer

export const connect = (WrappedComponent) => {
  return class ConnectClass extends React.Component {
    render () {
      return (
        <StoreContext.Consumer>
          {
            state => <WrappedComponent {...state} {...this.props} />
          }
        </StoreContext.Consumer>
      )
    }
  }
}

我们封装了一个 connect 函数,作为 HOC,用法和 react-redux的 connect 的单参形式形似。

那么我们在组件内应该如何使用呢?

Class Component

import React from 'react'
import { connect } from 'store'

export default @connect class Component extends React.Component {
  handleClick = () => {
    this.props.dispatch({type: 'set', payload: {a: 1}})
  }

  render() {
    return (<div>
      <div onClick={this.handleClick}>{JSON.stringify(this.props)}</div>
      <div>{JSON.stringify(this.props)}</div>
    </div>)
  }
}

Functional Component


import React from 'react'
import { useStore } from 'store'

export default const Component = () => {
  const [store, dispatch] = useStore()
  const handleClick = () => {
    dispatch({type: 'set', payload: {a: 1}})
  }

  return (<div>
      <div>{JSON.stringify(store)}</div>
      <div onClick={handleClick}>{JSON.stringify(store)}</div>
    </div>)
}

至此,我们就实现了基于 react 原生 api 的跨组件通信,hooks 和 hoc 的访问都通过 context api 实现,默认支持了浅比较。

    原文作者:vico
    原文地址: https://segmentfault.com/a/1190000019301519
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞