So, I guess the parent doesn't re-render the child component when the useState is updated. But the component should re-render automatically when using useState right ? Or I am missing something...
index.js
import { useEffect, useState, useRef } from "react";
import Clicking from "./clicking";
export default function IndexPage() {
const parentBtn = useRef(null);
const [clicks, setClicks] = useState(0);
useEffect(() => {
console.log("Clicks: ", clicks);
if (null !== parentBtn.current) {
parentBtn.current.addEventListener("click", () => setClicks(clicks + 1));
}
}, [clicks]);
return (
<div>
<p>Clicks number : {clicks}</p>
<Clicking clicks={clicks} setClicks={setClicks} />
<button ref={parentBtn}>Parent button</button>
</div>
);
}
clicking.js
import { useEffect, useRef } from "react";
const Clicking = ({ clicks, setClicks }) => {
const childBtn = useRef(null);
useEffect(() => {
childBtn.current.addEventListener("click", () => setClicks(clicks + 1));
}, []);
return <button ref={childBtn}>Child button</button>;
};
export default Clicking;
Does anyone know how to fix that ?
Here a codesandbox of the case : https://codesandbox.io/s/pensive-sun-9993d
Thanks for the help !
The issue is that your useEffect
only runs once, and therefore only receives the initial value of clicks
.
What you need to do is stop passing the clicks
value to the child and simply get the state value from the setClicks
function, as your updates depends on the previous state value anyway.
You should also pass setClicks
as a dependency to useEffect
, even though it is not technically mandatory in this case as the function is memoized and therefore will never change, but it's a best practice and you will avoid bugs in the future if you do it.
However the useEffect
in the child is useless in your case anyway and you would be better off simply passing a onClick
prop to the child that will trigger the parent's function to update the counter when the button is clicked.
The same thing is true for the parent though, and you could simply use the onClick
prop instead of using useRef
and end up with this much simpler code:
index.js
import { useState } from "react";
import Clicking from "./clicking";
export default function IndexPage() {
const [clicks, setClicks] = useState(0);
const updateClicksCounter = () => {
setClicks(clicks => clicks + 1);
};
return (
<div>
<p>Clicks number : {clicks}</p>
<Clicking onClick={updateClicksCounter} />
<button onClick={updateClicksCounter}>Parent button</button>
</div>
);
}
clicking.js
const Clicking = ({ onClick }) => {
return <button onClick={onClick}>Child button</button>;
};
export default Clicking;