Created
July 6, 2024 06:39
-
-
Save Armster15/94bde205dbb46315cd649fe76005c4b5 to your computer and use it in GitHub Desktop.
Imperatively update state in React. Based on logic from React Hot Toast
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
import { useStore, updateState } from './store'; | |
function App() { | |
const store = useStore(); | |
return ( | |
<div> | |
<button | |
onClick={() => { | |
updateState({ | |
...store, | |
modals: [...store.modals, Math.random()], | |
}); | |
}} | |
> | |
Click me! | |
</button> | |
<pre>{JSON.stringify(store)}</pre> | |
</div> | |
); | |
} | |
export default App; |
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
// Mostly derived from https://github.com/timolins/react-hot-toast/blob/main/src/core/store.ts | |
// | |
// The TLDR of how this works is the component that wants to receive state renders calls the `useStore` hook. | |
// This hook creates internal React state and stores the setState function in a global `listeners` variable. | |
// When `updateState`, an imperative function, is called, it iterates through all the listeners (aka setState functions) | |
// and calls them with the provided state. | |
// | |
// As complexity increases, it is recommended to use reducers and actions. See the cited React Hot Toast source file for more info, | |
// but reducers are simply pure functions that take in the previous state and an action and return the newly updated state. | |
import { useEffect, useState } from 'react'; | |
interface State { | |
modals: number[]; | |
} | |
let listeners: Array<(state: State) => void> = []; | |
let memoryState: State = { modals: [] }; | |
export function updateState(newState: State) { | |
memoryState = newState; | |
for (const listener of listeners) { | |
listener(memoryState); | |
} | |
} | |
export function useStore(): State { | |
const [state, setState] = useState<State>(memoryState); | |
useEffect(() => { | |
listeners.push(setState); | |
return () => { | |
const index = listeners.indexOf(setState); | |
if (index > -1) { | |
listeners.splice(index, 1); | |
} | |
}; | |
}, [setState]); | |
return state; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment