如何使用 refs 控制兄弟元素的行为?
How to control sibling elements' behaviour with refs?
我有 Modal
个带有信息框的组件。在每个 InfoBox
中,一个按钮(图标)显示有关产品特定成分的更多信息。该功能包含在每个 InfoBox
组件中。
当您单击一个信息框中的按钮(图标)时,我希望关闭所有其他信息框 - 如何使用 refs 控制同级元素的行为?
我正在尝试:
- 向下传递一个函数给每个信息框
- 当单击一个框时,发送该事件最多 parent 个元素(模态)
- 然后在 parent 元素中,遍历 refs 数组,找到图标被点击的那个,然后通过
useImperativeHandle
调用所有其他 refs 中的 closeShowMore
函数.
但我不知道如何在点击按钮的 refarray 中标记那个特定的 ref。
提前感谢您的宝贵时间!
PARENT:
const Modal = forwardRef(({ beer }, ref) => {
// stuff removed for brev
const [infoRef, setInfoRef] = useState([]);
const closeInfoBox = (valuePassedfromChildsEvent) => {
// here I would loop through infoRef, find the ref in which the button was clicked and then close the rest through useImperativeHandle()
};
useEffect(() => {
// create an array of refs for infoBoxes
setInfoRef(
Array(6)
.fill()
.map((_, i) => infoRef[i] || createRef())
);
}, []);
if (display) {
return (
<div className="modal-wrapper">
<div ref={backgroundRef} className="modal-background"></div>
<div className="modal-box">
<div className="info-box-wrapper">
<InfoBox
ref={infoRef[0]}
name="abv"
value={abv}
imageUrl={"../../assets/abv-white-2.svg"}
showMoreActive={closeInfoBox}
/>
<InfoBox
ref={infoRef[1]}
name="ibu"
value={ibu}
imageUrl={"../../assets/ibu-white.svg"}
showMoreActive={closeInfoBox}
/>
<InfoBox
ref={infoRef[2]}
name="fg"
value={target_fg}
imageUrl={"../../assets/style-white.svg"}
showMoreActive={closeInfoBox}
/>
// a few more of these
</div>
</div>
</div>
);
}
return null;
});
export default Modal;
CHILD:
const InfoBox = forwardRef((props, ref) => {
const [showMore, setShowMore] = useState(false);
const { name, value, imageUrl, showMoreActive } = props;
useImperativeHandle(ref, () => ({
openShowMore,
closeShowMore,
// is there a way to add an 'isActive' state/var here so I can access this from the loop in the parent and say if(!infoRef.current.isActive) {closeShowMore()}?
}));
const openShowMore = () => {
setShowMore(true);
showMoreActive(showMore) // I tried sending up this state value to parent to mark the active child but this always logs false when passed to parent, even when true. why?
};
const closeShowMore = () => {
setShowMore(false);
};
return (
<div className={`${name}`}>
<div className="info-box-container">
<div className="info-box-container-top">
<img src={imageUrl} alt="icon" />
<div className="info-wrapper">
<h4>{name}</h4>
<h5>{value}</h5>
</div>
<div
className="plus-minus-icon-wrapper"
onClick={() => openShowMore()}
>
{!showMore ? <GoPlus /> : <FiMinus />}
</div>
</div>
{showMore && (
<div className="info-box-conetiner-bottom">
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Officia
optio ab ullam! Porro molestias accusantium et laborum distinctio
</p>
</div>
)}
</div>
</div>
);
});
export default InfoBox;
你把它变得不必要地复杂了。
您想要的是使用尽可能少的状态,并将该状态保留在所有受影响组件的最小公共父级中。
在你的情况下,你想要做的是在 Modal 中有一个状态变量,它是这样的:
const [expandedInfoBoxName, setExpandedInfoBoxName] = useState(null);
您将这些道具传递给您的 InfoBox:
<InfoBox
name="ibu"
{your other props}
expandInfoBox={() => setExpandedInfoBoxName("ibu")
showMore={expandedInfoBoxName === "ibu"}
/>
在您的信息框中,您删除了状态和函数 useImperativeHandle、openShowMore 和 closeShowMore。
添加此以获得您的新道具:
const { name, value, imageUrl, expandInfoBox, showMore } = props;
然后在你的 jsx 中你做:
<div
className="plus-minus-icon-wrapper"
onClick={expandInfoBox}
>
就是这样。简而言之,您不想分散您的状态。总是问自己描述某事所需的最低状态是什么。
我有 Modal
个带有信息框的组件。在每个 InfoBox
中,一个按钮(图标)显示有关产品特定成分的更多信息。该功能包含在每个 InfoBox
组件中。
当您单击一个信息框中的按钮(图标)时,我希望关闭所有其他信息框 - 如何使用 refs 控制同级元素的行为?
我正在尝试:
- 向下传递一个函数给每个信息框
- 当单击一个框时,发送该事件最多 parent 个元素(模态)
- 然后在 parent 元素中,遍历 refs 数组,找到图标被点击的那个,然后通过
useImperativeHandle
调用所有其他 refs 中的closeShowMore
函数.
但我不知道如何在点击按钮的 refarray 中标记那个特定的 ref。
提前感谢您的宝贵时间!
PARENT:
const Modal = forwardRef(({ beer }, ref) => {
// stuff removed for brev
const [infoRef, setInfoRef] = useState([]);
const closeInfoBox = (valuePassedfromChildsEvent) => {
// here I would loop through infoRef, find the ref in which the button was clicked and then close the rest through useImperativeHandle()
};
useEffect(() => {
// create an array of refs for infoBoxes
setInfoRef(
Array(6)
.fill()
.map((_, i) => infoRef[i] || createRef())
);
}, []);
if (display) {
return (
<div className="modal-wrapper">
<div ref={backgroundRef} className="modal-background"></div>
<div className="modal-box">
<div className="info-box-wrapper">
<InfoBox
ref={infoRef[0]}
name="abv"
value={abv}
imageUrl={"../../assets/abv-white-2.svg"}
showMoreActive={closeInfoBox}
/>
<InfoBox
ref={infoRef[1]}
name="ibu"
value={ibu}
imageUrl={"../../assets/ibu-white.svg"}
showMoreActive={closeInfoBox}
/>
<InfoBox
ref={infoRef[2]}
name="fg"
value={target_fg}
imageUrl={"../../assets/style-white.svg"}
showMoreActive={closeInfoBox}
/>
// a few more of these
</div>
</div>
</div>
);
}
return null;
});
export default Modal;
CHILD:
const InfoBox = forwardRef((props, ref) => {
const [showMore, setShowMore] = useState(false);
const { name, value, imageUrl, showMoreActive } = props;
useImperativeHandle(ref, () => ({
openShowMore,
closeShowMore,
// is there a way to add an 'isActive' state/var here so I can access this from the loop in the parent and say if(!infoRef.current.isActive) {closeShowMore()}?
}));
const openShowMore = () => {
setShowMore(true);
showMoreActive(showMore) // I tried sending up this state value to parent to mark the active child but this always logs false when passed to parent, even when true. why?
};
const closeShowMore = () => {
setShowMore(false);
};
return (
<div className={`${name}`}>
<div className="info-box-container">
<div className="info-box-container-top">
<img src={imageUrl} alt="icon" />
<div className="info-wrapper">
<h4>{name}</h4>
<h5>{value}</h5>
</div>
<div
className="plus-minus-icon-wrapper"
onClick={() => openShowMore()}
>
{!showMore ? <GoPlus /> : <FiMinus />}
</div>
</div>
{showMore && (
<div className="info-box-conetiner-bottom">
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Officia
optio ab ullam! Porro molestias accusantium et laborum distinctio
</p>
</div>
)}
</div>
</div>
);
});
export default InfoBox;
你把它变得不必要地复杂了。 您想要的是使用尽可能少的状态,并将该状态保留在所有受影响组件的最小公共父级中。
在你的情况下,你想要做的是在 Modal 中有一个状态变量,它是这样的:
const [expandedInfoBoxName, setExpandedInfoBoxName] = useState(null);
您将这些道具传递给您的 InfoBox:
<InfoBox
name="ibu"
{your other props}
expandInfoBox={() => setExpandedInfoBoxName("ibu")
showMore={expandedInfoBoxName === "ibu"}
/>
在您的信息框中,您删除了状态和函数 useImperativeHandle、openShowMore 和 closeShowMore。 添加此以获得您的新道具:
const { name, value, imageUrl, expandInfoBox, showMore } = props;
然后在你的 jsx 中你做:
<div
className="plus-minus-icon-wrapper"
onClick={expandInfoBox}
>
就是这样。简而言之,您不想分散您的状态。总是问自己描述某事所需的最低状态是什么。