Skip to content

Instantly share code, notes, and snippets.

@Nathan-Schwartz
Last active August 16, 2020 05:04
Show Gist options
  • Save Nathan-Schwartz/ca762c192192b937d42568df986069f9 to your computer and use it in GitHub Desktop.
Save Nathan-Schwartz/ca762c192192b937d42568df986069f9 to your computer and use it in GitHub Desktop.
import React, {createContext, useReducer} from 'react';
import { Subtract } from 'utility-types';
import type { ReactNode } from 'react';
export default function buildStateManager<ContextState extends {}>(reducer: (ContextState, Object) => ContextState, initialState: ContextState) {
const context = createContext<{ state?: ContextState, dispatch?: Function }>(initialState);
const { Provider, Consumer } = context;
const StateProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
return <Provider value={{ state, dispatch }}>{children}</Provider>
};
// Consumer will render this component whenever state updates.
// This doesn't necessarily mean the new state is useful, so we diff the selected state.
class PureMiddle extends React.Component<{ name: string, children: ReactNode, state: Object}> {
shouldComponentUpdate(nextProps, nextState) {
return nextProps.state !== this.props.state
}
render() {
return this.props.children;
}
}
type InjectedProps = { contextValue: ContextState, contextDispatch: Function };
const connectToState = <Props extends InjectedProps>(mapStateToPropsSelector) =>
(BaseComponent: React.ComponentType<Props>) =>
React.memo((props: Subtract<Props, InjectedProps>) => {
// console.log('ConsumerWrapper rerenderred');
return (
<Consumer>
{({ state, dispatch }) => {
// console.log('Consumer FaCC renderred');
const newContextSelection = mapStateToPropsSelector(state)
return (
<PureMiddle
state={newContextSelection}
name={BaseComponent.name}
children={
<BaseComponent
{...props as Props}
contextValue={newContextSelection}
contextDispatch={dispatch}
/>
}
/>
);
}}
</Consumer>
)
});
return { StateProvider, connectToState, context };
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment