根据父组件状态更新子组件状态 React 函数式组件

4
假设我们有一个组件Accordion,它有一个内部状态isOpen,因此您可以关闭和打开这个组件。
现在我们想要一个父组件,它也有一个状态isOpen,并且有一个按钮。在这个组件中,我们有2个Accordion并将isOpen传递给Accordion,如果父组件改变状态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>
    );
  };

在这种情况下,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>
    );
  };

在这种情况下,当父组件改变isOpen状态时,子组件将被正确更新。
但是有一个问题:
“React Hook useEffect has a missing dependency: 'isOpen'. Either include it or remove the dependency array react-hooks/exhaustive-deps”
那么如何消除esLint报告的这个问题呢?我们不想将isOpen放入依赖数组中,因为这会导致错误。
如果我们像这样将isOpen添加到数组中:
 useEffect(() => {
      if (parentIsOpen !== isOpen) {
        setIsOpen(parentIsOpen);
      }
    }, [parentIsOpen, isOpen]);

我们会有这样一种情况,当我们点击手风琴(accordion)中的内部按钮并更新内部状态时,useEffect将运行并发现父组件与子组件具有不同的状态,然后立即设置旧状态。
因此,实际上你会进入一个循环,手风琴将永远不会打开。
问题是基于父组件的状态,如何最好地更新子组件的状态?
请不要建议将所有状态都放在父组件中并传递不带子状态的 props。由于此示例中的两个手风琴必须拥有自己的状态并能够以独立的方式打开和关闭,但如果父组件说关闭或打开,它应该听取。
谢谢!
2个回答

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>
    );
  };

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

今天测试过了,如果状态不同,则进行检查,如果没有,则React会注意不触发重新渲染,如果更新的状态与之前相同。


2

你不希望建议的东西实际上是我会提供的解决方案...你需要一个状态来控制父组件的isOpen。此外,您应该在父级中拥有单独的方法来控制每个手风琴的状态,并将其传递给每个手风琴的props...

我不确定为什么你想要子组件的单独状态。我认为类似这样的东西就足够了。

const MasterComponent = () => {
  const [isOpen, setIsOpen] = useState(false);
  const [isOpen1, setIsOpen1] = useState(false);
  const [isOpen2, setIsOpen2] = useState(false);

  const handleParentClose = () => {
    setIsOpen(false);
    setIsOpen1(false);
    setIsOpen2(false);
  }

  return (
    <div>
      <button onClick={handleParentClose}>toggle isOpen parent</button>
      <Accordion isOpen={isOpen1} setIsOpen={setIsOpen1} />
      <Accordion isOpen={isOpen2} setIsOpen={setIsOpen2} />
    </div>
 );
};

const Accordion = props => {
 return (
   <div>
    I'm open: {props.isOpen}
    <button onClick={props.setIsOpen}>toggle isOpen child</button>
   </div>
   );
  }

这不包括实际可见性切换的代码,但状态已经存在,包括在父元素关闭时关闭手风琴的状态。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接