Skip to content

Instantly share code, notes, and snippets.

@Munawwar
Created July 26, 2020 19:56
Show Gist options
  • Save Munawwar/e186473dc5c65efe9cb205b5a0230da2 to your computer and use it in GitHub Desktop.
Save Munawwar/e186473dc5c65efe9cb205b5a0230da2 to your computer and use it in GitHub Desktop.
global state manager for neverland/uland
<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