从父级传递给子级并在子级中使用的 useState 不会触发子级的重新渲染
useState passed from parent to child and used in child doesn't trigger the re-render of the child
所以,我猜父组件不会在 useState 更新时重新渲染子组件。
但是组件应该在使用 useState 时自动重新渲染,对吗?
或者我遗漏了什么...
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;
有谁知道如何解决这个问题?
这里是案例的codesandbox:https://codesandbox.io/s/pensive-sun-9993d
感谢您的帮助!
我找到了解决方案,在 child 中我使用了 setClicks
我换了
setClicks(clicks + 1)
来自
setClicks(val => val + 1)
并且有效。
也许如果有人对反应生命周期或其他有解释?
问题是您的 useEffect
只运行一次,因此只收到 clicks
的初始值。
您需要做的是停止将 clicks
值传递给 child 并简单地从 setClicks
函数获取状态值,因为您的更新无论如何都取决于先前的状态值。
您还应该将 setClicks
作为 useEffect
的依赖项传递,尽管在这种情况下这在技术上不是强制性的,因为函数已被记忆,因此永远不会改变,但这是最佳实践如果你这样做,你将来会避免错误。
但是 child 中的 useEffect
无论如何对你的情况都是无用的,你最好只是将 onClick
道具传递给 child 将触发单击按钮时 parent 更新计数器的函数。
parent 也是如此,你可以简单地使用 onClick
属性而不是 useRef
并最终得到这个更简单的代码:
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;
所以,我猜父组件不会在 useState 更新时重新渲染子组件。 但是组件应该在使用 useState 时自动重新渲染,对吗? 或者我遗漏了什么...
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;
有谁知道如何解决这个问题?
这里是案例的codesandbox:https://codesandbox.io/s/pensive-sun-9993d
感谢您的帮助!
我找到了解决方案,在 child 中我使用了 setClicks
我换了
setClicks(clicks + 1)
来自
setClicks(val => val + 1)
并且有效。 也许如果有人对反应生命周期或其他有解释?
问题是您的 useEffect
只运行一次,因此只收到 clicks
的初始值。
您需要做的是停止将 clicks
值传递给 child 并简单地从 setClicks
函数获取状态值,因为您的更新无论如何都取决于先前的状态值。
您还应该将 setClicks
作为 useEffect
的依赖项传递,尽管在这种情况下这在技术上不是强制性的,因为函数已被记忆,因此永远不会改变,但这是最佳实践如果你这样做,你将来会避免错误。
但是 child 中的 useEffect
无论如何对你的情况都是无用的,你最好只是将 onClick
道具传递给 child 将触发单击按钮时 parent 更新计数器的函数。
parent 也是如此,你可以简单地使用 onClick
属性而不是 useRef
并最终得到这个更简单的代码:
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;