从父级传递给子级并在子级中使用的 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;