Skip to content

Instantly share code, notes, and snippets.

@frzi
Last active October 31, 2022 18:43
Show Gist options
  • Save frzi/c9de4fa77f59ff1fb465265e0b227761 to your computer and use it in GitHub Desktop.
Save frzi/c9de4fa77f59ff1fb465265e0b227761 to your computer and use it in GitHub Desktop.
useForceUpdate in React with hooks.
// Put me somewhere accessible.
const useForceUpdate = () => {
const [_, setState] = useState(0)
return () => setState(val => val + 1)
}
// Example.
const Component = () => {
const forceUpdate = useForceUpdate()
const onClick = () => forceUpdate()
return (
<div onClick={onClick}>
{Date.now()}
</div>
)
}
@DRFR0ST
Copy link

DRFR0ST commented Apr 8, 2021

Hi over there! ☺️

I've found a problem with your hook and would like to explain it a little here. There might be a situation where you call setState(!state) and the state is not yet the most current value which might result in React deciding to not rerender the hook. To prevent this from happening, you can use the most current value provided from the setState function when passing a callback: (s) => !s. Here is an updated version:

export const useForceUpdate = () => {
  const state = useState(false);
  return () => state[1]((s) => !s);
};

There is also another way that I prefer.. React is only doing shallow comparison for values. That means you can set an empty object instead of a boolean. This will force React to rerender under any condition (since {} !== {}), the code might look like this:

export const useSignal = () => {
    const [, updateState] = useState();
    return useCallback(() => updateState({}), []);
}

PS. Pulled the example from my old code and I'm pretty sure the useCallback is just doing harm instead of making it faster.. A thing to find out I guess. 😃

Hope this makes sense, if not, please correct me 😄
See you around!

@frzi
Copy link
Author

frzi commented Apr 8, 2021

Yep! I actually ran into a similar issue as a matter of fact. Completely forgot I still had this gist somewhere rotting, collecting dust.
The fix is enitrely the same as yours:

export const useForceUpdate = () => {
	const [_, setState] = useState(false)
	return () => setState(val => !val)
}

Thanks for reminding me this broken piece of code was still publicly available. I'll update it 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment