Created
July 26, 2020 19:56
-
-
Save Munawwar/e186473dc5c65efe9cb205b5a0230da2 to your computer and use it in GitHub Desktop.
global state manager for neverland/uland
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
<html lang="en"> | |
<head> | |
<script src="https://unpkg.com/[email protected]/min.js"></script> | |
<script> | |
const { useState, useEffect } = neverland; | |
const globalStateManager = (() => { | |
// "The" global store | |
let store = {}; | |
// internal publisher-subscriber system to | |
// notify containers of store changes. | |
const pubsub = { | |
handlers: [], | |
subscribe(handler) { | |
// console.log('subscribed'); | |
if (!this.handlers.includes(handler)) { | |
this.handlers.push(handler); | |
} | |
}, | |
unsubscribe(handler) { | |
// console.log('unsubscribed'); | |
const index = this.handlers.indexOf(handler); | |
if (index > -1) { | |
this.handlers.splice(index, 1); | |
} | |
}, | |
notify(newStore) { | |
this.handlers.forEach(handler => handler(newStore)); | |
} | |
}; | |
const getStates = () => { | |
return { ...store }; | |
}; | |
const updateStates = (partial) => { | |
const newStore = { | |
...store, | |
...partial, | |
}; | |
store = newStore; | |
pubsub.notify(newStore); | |
}; | |
const useGlobalState = (propName) => { | |
let [state, setState] = useState(store[propName]); | |
useEffect(() => { | |
const newStateHandler = (newStore) => { | |
const newState = newStore[propName]; | |
// console.log('current state', state); | |
// console.log('new state', newState); | |
// console.log('isEqual', state === newState); | |
if (state !== newState) { | |
setState(newState); | |
state = newState; // prevent stale closure in case of multiple updates | |
} | |
}; | |
pubsub.subscribe(newStateHandler); | |
// on component unmount, unsubscribe to prevent mem leak | |
return () => pubsub.unsubscribe(newStateHandler); | |
// eslint-disable-next-line react-hooks/exhaustive-deps | |
}, [state, propName]); | |
return [ | |
state, | |
(newState) => updateStates({ [propName]: newState }) | |
] | |
}; | |
return { | |
useGlobalState, | |
getStates, | |
updateStates, | |
}; | |
})(); | |
</script> | |
</head> | |
<body> | |
<div id="app"></div> | |
<script type="module"> | |
const { neverland: $, render, html, useState } = neverland; | |
const { useGlobalState, updateStates, getStates } = globalStateManager; | |
// put some sane inits for global states on app launch | |
updateStates({ 'session.counter': 0 }); | |
console.log(getStates()); | |
const InputComp = () => { | |
const [count, setCount] = useGlobalState('session.counter'); | |
return html`<input oninput=${() => setCount(count + 1)} />`; | |
}; | |
const App = $(() => { | |
const [count, setCount] = useGlobalState('session.counter'); | |
return html`${ | |
[1,2].map(() => html` | |
<div id="count">${count}</div> | |
${$(InputComp)()} | |
<button type="button" onclick=${() => setCount(count + 1)}> | |
Increment | |
</button> | |
`) | |
}`; | |
}); | |
render(document.getElementById('app'), App()); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment