Skip to content

Instantly share code, notes, and snippets.

@ikawka
Last active April 29, 2020 07:57
Show Gist options
  • Save ikawka/c3c86981ad8938b201fe22c5df6af97b to your computer and use it in GitHub Desktop.
Save ikawka/c3c86981ad8938b201fe22c5df6af97b to your computer and use it in GitHub Desktop.
Global Store with React Context
/*
===== store.js =====
import createStore from '@store'
const initialState = { foo: 'bar' }
export const { Provider, connect } = createStore(initialState)
===== parent.js =====
import {Provider} from './store'
const Parent = () => {
return (
<Provider>
<Child />
</Provide>
)
}
export default Parent
===== child.js =====
import {connect} from './store'
const Child = ({ foo, updateStore }) => {
return (
<>
<div>{foo}</div>
<div><a href="#" onClick={() => updateStore({foo: 'Hello World'})}>Update!</a></div>
</>
)
}
const stateToProps = ({ foo }) => ({ foo })
export default connect(stateToProps)(Child)
*/
import React, { createContext, ComponentType, ReactNode, ReactElement } from 'react'
type State = { [key: string]: any }
type ProviderType = ComponentType<any>
type CreateProvider = (props: {
initialState: State
Provider: ProviderType
}) => ComponentType<any>
type Context = {
Consumer: Consumer
Provider: ProviderType
}
type Consumer = ComponentType<{
children: (state: State | void) => ReactNode
}>
type MapStateToProps = (state: State) => State
type Connect = (mapStateToProps: MapStateToProps) => (WrappedComponent: ComponentType<{}>) => ComponentType<{}>
type CreateConnect = (Consumer:Consumer) => Connect
type UpdateStore = (value: State, callback: () => {}) => void
const createProvider: CreateProvider = ({initialState, Provider}) => {
return class extends React.Component<{ children: ReactNode, initialState: {} }, State> {
constructor(props:any){
super(props)
this.state = props.initialState || initialState
}
updateStore: UpdateStore = (value, callback) => {
const _state = this.state
this.setState(Object.assign({..._state}, {...value}), callback)
}
render() {
return(
<Provider value={{store: this.state, updateStore: this.updateStore}}>
{this.props.children}
</Provider>
)
}
}
}
const creatConnect: CreateConnect = Consumer => stateToProps => WrappedComponent => {
const ConnectedComponent = (props:any) => (
<Consumer>
{context => {
const { store, updateStore } = context || {}
const filteredState = stateToProps(store || {})
return <WrappedComponent {...props} {...filteredState} updateStore={updateStore} />
}}
</Consumer>
)
return ConnectedComponent
}
const createStore = (initialState:State) => {
const context: Context = createContext({})
const Provider = createProvider({ initialState, Provider: context.Provider})
const connect = creatConnect(context.Consumer)
return { Provider, connect }
}
export default createStore
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment