Created
July 8, 2020 01:57
-
-
Save evatrium/a71a101b56605886a9ca70349bf69dfd to your computer and use it in GitHub Desktop.
simple global state for react
This file contains 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
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