|
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')); |