Last active
April 29, 2020 07:57
-
-
Save ikawka/c3c86981ad8938b201fe22c5df6af97b to your computer and use it in GitHub Desktop.
Global Store with React Context
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
===== 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