假设我们有一个组件Accordion,其内部状态为open,因此您可以关闭和打开该组件。
我们现在想要一个父组件,它也有一个状态isOpen和一个按钮。在这个组件中,我们有两次Accordion,我们将传递给AccordionisOpen,我们希望如果父级更改状态isOpenAccordi翁接受这个。
所有组件都是功能组件
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道具进行第一次渲染。如果我们按下按钮切换为Open 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>
);
};
在这种情况下,当父级更改为"打开"状态时,将正确更新子级。
有一个问题:
"React Hook useEffect缺少依赖项:"isOpen"。要么包括它,要么删除依赖数组react hook/穷举deps";
那么,如何删除esLint正在抱怨的这个问题,我们不想把isOpen放在其中,因为它会导致bug。
如果我们像这样将isOpen添加到数组中:
useEffect(() => {
if (parentIsOpen !== isOpen) {
setIsOpen(parentIsOpen);
}
}, [parentIsOpen, isOpen]);
然后我们会遇到这样的情况,我们会点击手风琴中的内部按钮并更新内部状态,然后运行useEffect,看到父级的状态与子级的状态不同,并立即设置旧状态。
所以基本上你有一个循环,手风琴永远不会打开。
问题是,基于父状态更新子状态的最佳方式是什么?
请不要建议将所有状态都放在父状态并在没有子状态的情况下传递道具。这是行不通的,因为在这个例子中,两个Accorsions都必须有自己的状态,并且能够以独立的方式打开和关闭,但如果家长说关闭或打开,它应该听从。
谢谢!
实际上我想说这是的方法
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会注意不要触发重新发布。
您所说的不建议的实际上是我提供的解决方案…您需要状态来控制父组件的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>
);
}
这不包括实际可见性切换的代码,但状态是存在的,包括在父关闭时关闭手风琴的状态。