Introduction
I wrote several blog posts about my learning process of the render props pattern. With every blog post I learned something new, but also made mistakes. But that's what learning is about. And because I am learning in public, it kind of forces me to fix things that could be better.
In my previous post (part 3) about the render props pattern, I thought I kind of
finished my implementation of the Toggle
component. However, I used the useEffect
hook, which is a great learning experience, but now, a few months later, I realized
in this case, it is not necessary to use useEffect
for what I wanted to achieve.
In this blog post I will explain you why.
Do you haz teh codez?
Sure, this is my Toggle
component from part 3:
function Toggle({ render, onToggle }) {
const [on, setOn] = useState(false);
function doToggle() {
setOn(!on);
}
useEffect(() => {
if (onToggle) onToggle(on);
}, [on]);
return <>{render(doToggle)}</>;
}
The Toggle
component receives two props. The first is the render
prop, which
is the function that renders the UI. That render
function is returned by the
Toggle
component, because that is what React components do, they always return
a piece of UI.
When calling the render
function in the return the doToggle
function is passed.
That function is called when the user toggles the UI. When a toggle takes place
the on
state of the Toggle
component is inversed, true becomes false and false
becomes true.
The useEffect
responds to the fact the value of the on
state and calls the second
prop that is passed in the Toggle
component: the onToggle
. This is a callback
function passed by the component that uses the Toggle
component. What typically happens
when calling the onToggle
is that there happens some kind of state change in the
component that uses the Toggle
component.
Why useEffect?
As soon as the on
state value changes, I use the useEffect
to pass that new state
value to the callback function so the parent component also can use that new value.
I thought I had to "wait" for the state change to really have happened before I could call the callback function, because that's the moment I know the new state value for sure.
However, at the moment I set the new on
value with setOn
, I already know the
new value! It's right there in the code where I pass it to setOn
! 8-|
DOH!!!
So why not call the callback right away with that same value:
function Toggle({ render, onToggle }) {
const [on, setOn] = useState(false);
function doToggle() {
setOn(!on);
onToggle(!on);
}
return <>{render(doToggle)}</>;
}
Oh, and by the way... There was also a bug in my
useEffect
code. TheuseEffect
is calling theonToggle
callback function which makes it a dependency and therefore should be added to the dependency array. However, we've removed theuseEffect
entirely, so that's not an issue anymore.
How I figured this out
After writing part 3 it kept on disturbing me I had to use such a complicated solution for such a common and simple problem. So I started googling arround and stumbled upon this Reddit post.
There Mr Dan Abramov himself explains why you can not have the new state value right away. And also that this is not a problem most of the times as you define the new state value yourself and then can do with it whatever you want.
He explains with code that looks something like this:
const [count, setCount] = useState(0);
function handleClick() {
setCount(42);
// you can't expect this to somehow immediately change the count, we even declared it with const :-)
console.log(count); // 0
// but on next render, count will be 42
}
I think the code comments explain a lot. :)
Conclusion
By writing this blog I not only learned something new, I also fixed crappy code and, last
but not least, have a better understanding of the useState
and useEffect
hooks.
And I realized that learning in public can be a bit awkward, but more importantly, learning by sharing is a great way to solidify what you learn.
To see the code, check out this CodeSandbox or the Github repo.