开发者问题收集

为什么 useRef 永远不会从 null 更新 current?

2021-06-13
1166

问题: 无法将 ref 从 {current: null} 更新为组件上的实际 ref。

我希望发生的情况: 据我所知,{current: null} 应该更新以包含 ref 所在的 div,以便能够单击其外部(最终关闭它)。9 了解它不会在第一次渲染时更新,但它永远不会更新。它在页面加载时运行两次,都返回 current: null。

我尝试过: 我已遵循所有 SO 建议使用 useEffect,然后最终将其分离到此函数中,这似乎是执行此操作最合适和最新的方法。它只是从不更新当前。

function useOutsideAlerter(ref) {
  useEffect(() => {

    function handleClickOutside(event) {
      if (ref.current && !ref.current.contains(event.target)) {
        console.log(ref);
      } else {
        console.log("else", ref);
      }
    }
    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, []);
}

export const Modal = (props) => {
  const [showModal, setShowModal] = useState(props.showModal);
  const wrapperRef = useRef(null);
  useOutsideAlerter(wrapperRef);

  return (
    <Layout>
      <ModalOuter
        showModal={showModal || props.showModal}
        id={styles["modalOuter"]}
        handleClose={props.handleClose}
      >
        <ModalInner
          ref={wrapperRef}
          handleClose={props.handleClose}
        >
          <Layout display="flex" flexDirection="column">
            <Layout display="flex" flexDirection="column">
              <ModalTitle title={props.title} />
            </Layout>
            <HR />
            <Layout display="flex" flexDirection="column">
              <ModalBody body={props.body} />
            </Layout>
          </Layout>
        </ModalInner>
      </ModalOuter>
    </Layout>
  );
};

ModalInner

    export const ModalInner = (props) => {
  return (
    <Layout
      id={props.id}
      ref={props.ref}
      display="flex"
      justifyContent="center"
      alignItems="center"
      padding="2rem"
      margin="2rem"
      backgroundColor="white"
    >
      {props.children}
    </Layout>
  );
};

布局组件

export const Layout = (props) => {
  return (
    <div
      id={props.id}
      ref={props.ref}
...
1个回答

问题

在 React 中,有几个特殊的“props”, refkey 就是其中的几个。我在 props 两边加上了引号,因为虽然它们是作为 props 传递的,但它们不会传递到子组件中的 props 对象上,也无法在该对象上访问。

解决方案

使用 React.forwardRef 将任何传递的 React refs 转发到功能组件,并在子组件中公开它们。

export const ModalInner = React.forwardRef((props, ref) => { // <-- access ref
  return (
    <Layout
      id={props.id}
      ref={ref} // <-- pass ref *
      display="flex"
      justifyContent="center"
      alignItems="center"
      padding="2rem"
      margin="2rem"
      borderRadius="5px"
      backgroundColor="white"
      border={`1px solid ${Color.LightGray}`}
      boxShadow={`0rem 0rem 1rem white`}
    >
      {props.children}
    </Layout>
  );
});

* 注意: Layout 和子组件同样需要转发 ref ,直到它实际附加到 DOMNode 为止。

另一种解决方案是将 ref 作为普通 prop 传递。

<ModalInner
  wrapperRef={wrapperRef}
  handleClose={props.handleClose}
>

...

export const ModalInner = (props) => {
  return (
    <Layout
      id={props.id}
      wrapperRef={props. wrapperRef} // <-- pass wrapperRef prop
      display="flex"
      justifyContent="center"
      alignItems="center"
      padding="2rem"
      margin="2rem"
      borderRadius="5px"
      backgroundColor="white"
      border={`1px solid ${Color.LightGray}`}
      boxShadow={`0rem 0rem 1rem white`}
    >
      {props.children}
    </Layout>
  );
};

同样,您需要钻取 wrapperRef 一直传递到子节点,直到到达附加 ref 的实际 DOMNode。

示例

<div ref={props.wrapperRef> .... </div>

您可能还会发现 Refs 和 DOM 文档对于使用 React refs 很有用。

Drew Reese
2021-06-13