Skip to content

Instantly share code, notes, and snippets.

@drodsou
Created March 27, 2022 05:05
Show Gist options
  • Save drodsou/d6ee480489bf9965dde4ec14d1082f75 to your computer and use it in GitHub Desktop.
Save drodsou/d6ee480489bf9965dde4ec14d1082f75 to your computer and use it in GitHub Desktop.
React useSharedState (Jotai inspired)
// like useState but shared by several compoments
// inspired by Jotai,
function createUseSharedState(value) {
let _value = value;
const _subs = new Map()
function useSharedState() {
const self = React.useRef({});
const selfForceUpdate = React.useReducer(x=>!x, false)[1]
React.useEffect(()=>{
console.log('sub')
_subs.set(self.current, selfForceUpdate)
return ()=>{
console.log('unsub')
_subs.delete(self.current);
}
},[])
return [
_value,
(fnNewValue)=>{
_value = fnNewValue(_value);
_subs.forEach((v,k)=>{
console.log('sub exec');
v() // forceupdates
});
}
]
}
return useSharedState;
}
// -- EXAMPLE: Initialize, outside a React comonot
const useCounter = createUseSharedState(3);
// -- and use it in a component like useState
const Comp1 = ()=>{
const [count, setCount] = useCounter();
return <button onClick={()=>setCount(s=>s+1)}>{count}</button>
}
// See live example: https://codepen.io/drodsou/pen/dyJvaNq
// Online example: https://codepen.io/drodsou/pen/dyJvaNq
// Local example:
// npm create vite@latest example1 -- --template react && cd example1 && npm install
// paste this in src/App.jsx
import React from 'react';
// -- lib
function createUseSharedState(value) {
let _value = value;
const _subs = new Map()
function useSharedState() {
const self = React.useRef({});
const selfForceUpdate = React.useReducer(x=>!x, false)[1]
React.useEffect(()=>{
console.log('sub')
_subs.set(self.current, selfForceUpdate)
return ()=>{
console.log('unsub')
_subs.delete(self.current);
}
},[])
return [
_value,
(fnNewValue)=>{
_value = fnNewValue(_value);
_subs.forEach((v,k)=>{
console.log('sub exec');
v() // forceupdates
});
}
]
}
return useSharedState;
}
// -- Example
const useCounter = createUseSharedState(3);
// window.useCounter = useCounter;
function App () {
const [appState, setAppState] = React.useState(true)
const Comp1 = ()=>{
// const [count, setCount] = React.useState(0);
const [count, setCount] = useCounter();
return <button onClick={()=>setCount(s=>s+1)}>{count}</button>
}
const Comp2 = ()=>{
// const [count, setCount] = React.useState(0);
const [count, setCount] = useCounter();
return <button onClick={()=>setCount(s=>s+1)}>{count}</button>
}
return (
<>
<h1>useSharedState</h1>
<p>Open console to see traces</p>
<button onClick={()=>setAppState(s=>!s)}> toggle counters</button>
{appState && <div>
<Comp1/>
<Comp2/>
</div>}
<style dangerouslySetInnerHTML={{__html: `
body { background-color: black; color: white; font-family: sans-serif;}
button {font-size:20px; margin: 10px; padding: 5px 20px; cursor: pointer;}
`}}/>
</>
)
}
export default App;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment