Skip to content

Instantly share code, notes, and snippets.

@mgtitimoli
Created August 23, 2019 21:18
Show Gist options
  • Save mgtitimoli/2899a1e563bea739456b59eb19801123 to your computer and use it in GitHub Desktop.
Save mgtitimoli/2899a1e563bea739456b59eb19801123 to your computer and use it in GitHub Desktop.
// @flow
import React, {createContext, useContext, useState} from "react";
import type {Context, Node, StatelessFunctionalComponent} from "react";
import useEffectOnUpdate from "./useEffectOnUpdate";
type UseStateStateUpdater<TState> = (state: TState) => TState;
type UseStateUpdateState<TState> = (
stateOrUpdater: TState | UseStateStateUpdater<TState>
) => void;
type StoreProviderProps<TValue> = {|children: Node, value: TValue|};
type StoreProviderType<TValue> = StatelessFunctionalComponent<
StoreProviderProps<TValue>
>;
type Store<TValue> = {|
update: UseStateUpdateState<TValue>,
value: TValue
|};
type StoreHook<TValue> = () => Store<TValue>;
type StoreConsumerProps<TValue> = {|children: (store: Store<TValue>) => Node|};
type StoreConsumerType<TValue> = StatelessFunctionalComponent<
StoreConsumerProps<TValue>
>;
type CreateStoreResult<TValue> = {|
StoreConsumer: StoreConsumerType<TValue>,
StoreProvider: StoreProviderType<TValue>,
useStore: StoreHook<TValue>
|};
const createStoreHook = <TValue>(
StoreContext: Context<Store<TValue>>
): StoreHook<TValue> => () => useContext(StoreContext);
const useStoreForProvider = <TValue>(propValue: TValue): Store<TValue> => {
const [value, update] = useState<TValue>(propValue);
useEffectOnUpdate(() => update(propValue), [propValue]);
return {update, value};
};
const createStoreProvider = <TValue>(
StoreContext: Context<Store<TValue>>
): StoreProviderType<TValue> => ({children, value}) => (
<StoreContext.Provider value={useStoreForProvider(value)}>
{children}
</StoreContext.Provider>
);
const createStoreConsumer = <TValue>(
StoreContext: Context<Store<TValue>>
): StoreConsumerType<TValue> => props => <StoreContext.Consumer {...props} />;
const createStoreUsing = <TValue>(
StoreContext: Context<Store<TValue>>
): CreateStoreResult<TValue> => ({
StoreConsumer: createStoreConsumer(StoreContext),
StoreProvider: createStoreProvider(StoreContext),
useStore: createStoreHook(StoreContext)
});
const createStore = <TValue>(): CreateStoreResult<TValue> =>
createStoreUsing<TValue>(createContext<Store<TValue>>());
export default createStore;
// @flow
import {useEffect, useRef} from "react";
const useEffectUsing = (isMountedRef, create, inputs) =>
useEffect(() => {
if (isMountedRef.current) return create();
isMountedRef.current = true;
}, inputs);
const useEffectOnUpdate: typeof useEffect = (create, inputs) =>
useEffectUsing(useRef(false), create, inputs);
export default useEffectOnUpdate;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment