首页 > 解决方案 > 如何使用 refs 控制兄弟元素的行为?

问题描述

我有Modal带有信息框的组件。在每个InfoBox按钮(图标)中都显示了有关产品特定成分的更多信息。该功能包含在每个InfoBox组件中。

当您单击一个 InfoBox 中的按钮(图标)时,我希望所有其他 InfoBox(es) 关闭 - 如何使用 refs 控制兄弟元素的行为?

我试图:

  1. 将函数向下传递给每个 InfoBox
  2. 单击一个框时,将该事件发送到父元素(模态)
  3. 然后在父元素中,遍历引用数组,找到单击图标的那个,并在所有其他引用中useImperativeHandle调用该closeShowMore函数。

但我不知道如何在单击按钮的 refarray 中标记该特定 ref。

在此先感谢您的时间!

家长:

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;

孩子:

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;

标签: javascriptreactjsref

解决方案


你让它变得不必要的复杂。您想要的是使用尽可能少的状态,并将该状态保持在所有受影响组件的最小公共父级中。

在您的情况下,您想要做的是在 Modal 中有一个状态变量,如下所示:

const [expandedInfoBoxName, setExpandedInfoBoxName] = useState(null);

您将这些道具传递到您的信息框:

<InfoBox
    name="ibu"
    {your other props}
    expandInfoBox={() => setExpandedInfoBoxName("ibu")
    showMore={expandedInfoBoxName === "ibu"}
/>

在您的 InfoBox 中,您删除您的状态和函数 useImperativeHandle、openShowMore 和 closeShowMore。添加这个来获得你的新道具:

const { name, value, imageUrl, expandInfoBox, showMore } = props;

然后在你的 jsx 中你做:

<div
   className="plus-minus-icon-wrapper"
   onClick={expandInfoBox}
>

就是这样。简而言之,你不想分散你的状态。总是问自己,描述某事所需的最低限度是什么。


推荐阅读