比方说,我们有一个内部状态为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>
);
};
在这种情况下,以上的手风琴将首先呈现为父状态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将运行,并看到父级与子级具有不同的状态,并立即设置旧状态。
所以基本上,您有一个循环,手风琴将永远不会打开。
问题是基于父状态更新子状态的最佳方法是什么?
请不要建议将所有状态置于父状态,并传递没有孩子状态的道具。这将不起作用,因为此示例中的两个手风琴都必须具有自己的状态,并且能够以独立的方式打开和关闭,但是,如果父母说“关闭”或“打开”,则应听取它的声音。
谢谢!
答案 0 :(得分:1)
您要说的不建议实际上是我将提供的解决方案……您将需要状态来控制父组件的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>
);
}
这不包括用于实际可见性切换的代码,但状态在那里。
编辑:添加了在父关闭时关闭手风琴的代码。
答案 1 :(得分:0)
实际上我会说这是做到这一点的方式
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>
);
};
因此,只需删除子组件中的状态检查,让他更新状态即可,但是由于使用相同的值进行更新,因此不会重新渲染或发生某些昂贵的行为。
今天对其进行了测试,并进行了检查(如果状态不同或不相同),则如果更新的状态与以前相同,则应注意不要触发重新渲染。