Skip to content

Instantly share code, notes, and snippets.

@cmlarsen
Created July 6, 2020 19:14
Show Gist options
  • Save cmlarsen/89b6a72896f3088b7eb7bb8cfb3b8790 to your computer and use it in GitHub Desktop.
Save cmlarsen/89b6a72896f3088b7eb7bb8cfb3b8790 to your computer and use it in GitHub Desktop.
A bare bones shared state system using a pub/sub pattern and Reacts underlying useState hooks.
import React, {useState, useEffect, useRef, useCallback, useMemo} from 'react'
import * as ReactDOM from 'react-dom'
import {get, set} from 'lodash'
//The guts of the pub/sub system
const sharedState = {
state: {},
watchers:{},
watch (bitOfState, callback) {
if (!this.watchers[bitOfState]) {
this.watchers[bitOfState] = new Set();
}
this.watchers[bitOfState].add(callback)
callback(get(this.state, bitOfState))
},
unwatch (bitOfState, callback) {
this.watchers[bitOfState].delete(callback)
},
set(bitOfState, value) {
if (get(this.state,bitOfState) === value) return
set(this.state, bitOfState,value)
for (let callback of this.watchers[bitOfState]) {
callback(get(this.state,bitOfState))
}
},
init(initialState){
this.state = initialState
}
}
//The main intercace hook
function useSharedState(bitOfState) {
const [value, setValue] = useState(get(sharedState.state,bitOfState));
useEffect(() => {
const watcher = _value => {
setValue(_value)
}
sharedState.watch(bitOfState, watcher)
return () => {
sharedState.unwatch(bitOfState, watcher)
}
}, [])
const setter = useCallback((input) => {
let value;
if(typeof input === 'function'){
value = input(get(this.state,bitOfState))
} else {
value = input
}
sharedState.set(bitOfState, input)
},[])
return [value, setter]
}
function ExampleComponent1(){
const [time, setTime] = useSharedState('time')
const [counter, setCounter] = useSharedState('nested.kinda.deep')
const [weather, setWeather] = useSharedState('weather')
useEffect(()=>{
setInterval(()=>{
//setting state based on current value
setCounter(_value=>_value+1)
},1000)
},[])
useEffect(()=>{
fetch('https://api.weather.gov/gridpoints/TOP/31,80/forecast').then(res=>res.json()).then(result=>{
setWeather(result.properties.periods[0].shortForecast)
})
},[]);
return <div>1: {counter} @ { time.toISOString()} : {weather} </div>
}
function ExampleComponent2(){
const [time, setTime] = useSharedState('time')
const [counter] = useSharedState('nested.kinda.deep')
const [weather] = useSharedState('weather')
useEffect(()=>{
setInterval(()=>{
setTime(new Date())
},1000)
},[])
return <div>2: {counter} @ { time.toISOString()} : {weather} </div>
}
function App(){
sharedState.init({
time:new Date(),
weather:undefined,
nested:{
kinda:{
deep:0
}
}})
return <div className="App">
<header className="App-header">
<ExampleComponent1/>
<ExampleComponent2/>
</header>
</div>
}
ReactDOM.render(<App />, document.getElementById('root'));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment