Skip to content

Instantly share code, notes, and snippets.

@evatrium
Created July 8, 2020 01:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save evatrium/a71a101b56605886a9ca70349bf69dfd to your computer and use it in GitHub Desktop.
Save evatrium/a71a101b56605886a9ca70349bf69dfd to your computer and use it in GitHub Desktop.
simple global state for react
import {useCallback, useEffect, useState} from 'react';
const Subie = (subs = [], _unsub = it => subs.splice(subs.indexOf(it) >>> 0, 1)) => [
it => ((subs.push(it), () => _unsub(it))),
(...data) => subs.slice().map(f => (f(...data)))
];
const useForceUpdate = () => {
const [, set] = useState(Object.create(null));
return useCallback(() => {
set(Object.create(null))
}, [set]);
};
const shouldUpdate = (mapStateToProps, doRerender, getState) => {
let state = mapStateToProps(getState());
return () => {
let mapped = mapStateToProps(getState());
for (let i in mapped) if (mapped[i] !== state[i]) {
state = mapped;
return doRerender()
}
for (let i in state) if (!(i in mapped)) {
state = mapped;
return doRerender();
}
}
};
// linter complains about hooks, but hooks will be used after
// returned from the factory function createState
export const createState = (state = {}) => {
const [sub, notify] = Subie();
const getState = () => state;
return {
set: update => {
state = {
...state,
...(typeof update === 'function' ? update(state) : update)
};
notify();
},
use: (mapStateToProps) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
let doRerender = useForceUpdate();
//eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
let update = mapStateToProps
? shouldUpdate(mapStateToProps, doRerender, getState)
: doRerender;
return sub(update) //returns unsub
}, [doRerender]);
return state;
},
getState
}
};
/*
// usage
import React from 'react'
import {Modal} from './components';
import {createState} from './createState';
const modal = createState({
open: false,
Component: null
});
export const ModalControlledFromAnyWhere = () => {
const {open, Component} = modal.use()
return (
<Modal open={open}>
<Component/>
</Modal>
)
}
// some other file
modal.set({
open: true,
Component: ()=> <Confirm/>
})
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment