React Hooks 距离正式发布已经过去好几个月了,redux,mobx,也都支持了 Hooks 的用法,那么有没有可能用 React Context API & Hooks 来实现一个同时支持 class component 和 functional component 访问的 store 呢?答案是肯定的。
既然我们是基于 Context Api,那么先来创建一个 context 对象
// store.jsimport React from 'react'const initialStore = {}const StoreContext = React.createContext(initialStore)
接着我们来构造两种访问 store 的方法:
Hooks 的方式:
// store.jsimport {useReducer, useContext} from 'react'// 声明 reducerexport 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
// 构造一个根组件 Providerexport 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 实现,默认支持了浅比较。