根据父状态更新子状态反应功能组件

Kib*_*007 4 parent reactjs

假设我们有一个组件 Accordion,其内部状态为 isOpen,因此您可以关闭和打开该组件。

我们现在想要一个父组件,它也有一个状态 isOpen 并有按钮。在此组件中,我们有 2 个 Accordion,我们将其传递给 Accordion isOpen,并且我们希望如果父级更改状态 isOpen Accordion 接受此操作。

所有组件都是功能组件

 const Accordion = ({ isOpen: parentIsOpen = false }) => {
    const [isOpen, setIsOpen] = useState(parentIsOpen);
    const handleSetIsOpen = () => setIsOpen(!isOpen);
    return (
      <div>
        I'm open: {isOpen}
        <button onClick={handleSetIsOpen}>toggle isOpen child</button>
      </div>
    );
  };

  const MasterComponent = () => {
    const [isOpen, setIsOpen] = useState(false);
    const handleSetIsOpen = () => setIsOpen(!isOpen);
    return (
      <div>
        <button onClick={handleSetIsOpen}>toggle isOpen parent</button>
        <Accordion isOpen={isOpen} />
        <Accordion isOpen={isOpen} />
      </div>
    );
  };
Run Code Online (Sandbox Code Playgroud)

在上面的这种情况下,Accordion 将作为初始状态父 isOpen 属性进行第一次渲染。如果我们按下按钮切换 isOpen Parent,我们将更改父状态,但子状态不会更新。

为了解决这个问题,我们可以使用 useEffect

  const Accordion = ({ isOpen: parentIsOpen = false }) => {
    const [isOpen, setIsOpen] = useState(parentIsOpen);
    const handleSetIsOpen = () => setIsOpen(!isOpen);

    useEffect(() => {
      if (parentIsOpen !== isOpen) {
        setIsOpen(parentIsOpen);
      }
    }, [parentIsOpen]);

    return (
      <div>
        I'm open: {isOpen}
        <button onClick={handleSetIsOpen}>toggle isOpen child</button>
      </div>
    );
  };

  const MasterComponent = () => {
    const [isOpen, setIsOpen] = useState(false);
    const handleSetIsOpen = () => setIsOpen(!isOpen);
    return (
      <div>
        <button onClick={handleSetIsOpen}>toggle isOpen parent</button>
        <Accordion isOpen={isOpen} />
        <Accordion isOpen={isOpen} />
      </div>
    );
  };
Run Code Online (Sandbox Code Playgroud)

在这种情况下,当父级更改 isOpen 状态时,子级将被正确更新。

这有一个问题:

“React Hook useEffect 缺少依赖项:'isOpen'。要么包含它,要么删除依赖项数组react-hooks/exhaustive-deps”

那么如何消除 esLint 抱怨的这个问题,并且我们不想将 isOpen 放入其中,因为它会导致错误。

如果我们像这样将 isOpen 添加到数组中:

 useEffect(() => {
      if (parentIsOpen !== isOpen) {
        setIsOpen(parentIsOpen);
      }
    }, [parentIsOpen, isOpen]);
Run Code Online (Sandbox Code Playgroud)

然后我们会遇到这样一种情况,我们将单击折叠面板中的内部按钮并更新内部状态,然后 useEffect 将运行并看到父级与子级具有不同的状态,并将立即设置旧状态。

所以基本上你有一个循环,手风琴永远不会打开。

问题是根据父状态更新子状态的最佳方法是什么?

请不要建议将所有状态放入父级并传递没有子状态的道具。这是行不通的,因为这个例子中的两个手风琴都必须有自己的状态,并且能够以独立的方式打开和关闭,但是如果父级说关闭或打开,它应该听取它。

谢谢你!

Kib*_*007 5

实际上我会说这是这样做的方法

 const Accordion = ({ isOpen: parentIsOpen = false }) => {
    const [isOpen, setIsOpen] = useState(parentIsOpen);
    const handleSetIsOpen = () => setIsOpen(!isOpen);

    useEffect(() => {
        setIsOpen(parentIsOpen);
    }, [parentIsOpen]);

    return (
      <div>
        I'm open: {isOpen}
        <button onClick={handleSetIsOpen}>toggle isOpen child</button>
      </div>
    );
  };

  const MasterComponent = () => {
    const [isOpen, setIsOpen] = useState(false);
    const handleSetIsOpen = () => setIsOpen(!isOpen);
    return (
      <div>
        <button onClick={handleSetIsOpen}>toggle isOpen parent</button>
        <Accordion isOpen={isOpen} />
        <Accordion isOpen={isOpen} />
      </div>
    );
  };
Run Code Online (Sandbox Code Playgroud)

因此,只需删除子组件中的状态检查,让他更新状态,但由于使用相同的值进行更新,因此它不会重新渲染或执行一些昂贵的行为。

今天测试了一下,如果状态不同或没有相同,如果更新的状态与以前相同,React 会注意不触发重新渲染。