Skip to content

Instantly share code, notes, and snippets.

@Landerson352
Last active July 7, 2019 01:54
Show Gist options
  • Save Landerson352/3134abae5c981d6763398d54522c61a5 to your computer and use it in GitHub Desktop.
Save Landerson352/3134abae5c981d6763398d54522c61a5 to your computer and use it in GitHub Desktop.
createAppState: Lean app state management for small React apps, using hooks and immer. (after copying files: yarn add immer lodash)
// Wrap your app with your provider, and place components inside it
import React from 'react';
import {AppStateProvider} from './useAppState';
import MyComponent from './MyComponent';
const App = () => (
<AppStateProvider>
<MyComponent />
</AppStateProvider>
);
export default App;
// This utility module can be used in any project
import React, {createContext, useContext, useReducer} from 'react';
import produce from 'immer';
import {mapValues} from 'lodash';
const createAppState = (initialState = {}, mutators = {}) => {
const Context = createContext({});
// create provider
// sets initial state and uses Immer *as* the reducer
const AppStateProvider = ({children}) => (
<Context.Provider value={useReducer(produce, initialState)}>
{children}
</Context.Provider>
);
// create hook
// wraps each function with dispatch and injects state so that Immer can receive it
const useAppState = () => {
const [state, dispatch] = useContext(Context);
const mutatorsWithDispatch = mapValues(mutators, (mutator) => (...args) => {
return dispatch((state) => mutator(state, ...args));
});
return [state, mutatorsWithDispatch];
};
return [AppStateProvider, useAppState];
};
export default createAppState;
// Components like this one can access state and mutators, if they are used within the provider
import React from 'react';
import useAppState from './useAppState';
const MyComponent = () => {
// consume and modify state like so:
const [{name}, {setName}] = useAppState();
return (
<div>
<p>Your name is {name}.</p>
<button onClick={() => setName('Bob')}>My name is Bob!</button>
</div>
);
};
export default MyComponent;
// Define your application's intitial state and actions which may mutate it
import createAppState from './createAppState';
const initialState = {
name: 'Unknown',
};
const mutators = {
setName: (state, name) => {
// immer's "draft" state is frontloaded in the argument list, so you can manipulate it directly
state.name = name;
},
};
export const [AppStateProvider, useAppState] = createAppState(initialState, mutators);
export default useAppState;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment