React hooks & the closure hell 2
This is a continuation of my last post (React hooks & the closure hell)
Just a quick tl;dr
Functional components require you to regenerate all your callbacks on each re-render because there is nowhere to store them (In old class based components you could just bind your functions as method and you were good to go).
Previous solutions required you to either specify dependencies so they could be passed to existing function, or to work with objects that would store current properties and values. I think these solutions were cumbersome, so I kept tinkering and created even better solution!
Meet useCallbacks
const useCallbacks = (reinit) => {
const data = useRef({ callbacks: {}, handlers: {} })
const callbacks = data.current.callbacks
const handlers = data.current.handlers
// Generate new callbacks
reinit(callbacks)
// Generate new handlers if necessary
for (let callback in callbacks) {
if (!handlers[callback]) {
handlers[callback] = (...args) => callbacks[callback](...args)
}
}
// Return existing handlers
return handlers
}
Usage (Try here)
const App = () => {
const [value, setValue] = useState(1);
const handlers = useCallbacks(callbacks => {
callbacks.handleClick = (event) => {
setValue(value + 1)
}
})
// Check console, the state has changed so the App function will re-run
// but memoized ExpensiveComponent won't be called because the actual handling
// function hasn't changed.
console.log(value)
return (
<div className="app">
<ExpensiveComponent onClick={handlers.handleClick} />
<button onClick={handlers.handleClick}>
I will not trigger expensive re-render
</button>
</div>
);
};
And that's it!
You don't have to specify any dependencies or work with messy objects.
The callback is regenerated but the actual handling function is not, so your pure components or memoized components won't re-render unnecessarily.
Everything works as hooks intended!
Tell me what you think.
play with this code and see where it goes wrong