【问题标题】:Update child state based on parent state react functional components根据父状态更新子状态 react 功能组件
【发布时间】:2021-01-12 02:55:38
【问题描述】:

假设我们有一个组件 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>
    );
  };

在这种情况下,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 缺少依赖项:'isOpen'。要么包含它,要么删除依赖数组 react-hooks/exhaustive-deps”

那么如何解决这个 esLint 抱怨的问题,我们不想把 isOpen 放在这个里面,因为它会导致错误。

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

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

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

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

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

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

谢谢!

【问题讨论】:

    标签: reactjs parent


    【解决方案1】:

    其实我会说这是这样做的方法

     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】:

      你所说的不建议实际上是我提供的解决方案……你需要 state 来控制父组件的 isOpen。此外,您应该在父级中有单独的方法来控制每个手风琴的状态,并在道具中传递给每个手风琴......

      不确定为什么要为子组件设置单独的状态。我相信这样的事情就足够了。

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

      这不包括实际可见性切换的代码,但状态就在那里。

      编辑:添加了在父关闭时关闭手风琴的代码。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-03-23
        • 1970-01-01
        • 1970-01-01
        • 2020-08-12
        • 2018-10-09
        • 2021-01-03
        • 1970-01-01
        • 2021-01-24
        相关资源
        最近更新 更多