首页 > 解决方案 > 通过 Ref 创建 React Portal

问题描述

我想创建一个应该附加到它的容器组件的 Portal 组件,但不是通过容器的 ID 而是通过它的 ref。换句话说,我不想将 document.getElementById('CONTAINER_ID') 作为第二个参数传递给 ReactDOM.createPortal 函数,而是仅仅依赖 React.forwardRef 传递的容器的 ref。有没有一种简单的方法可以实现这一目标?否则,也许我需要先创建一个“附加”到 ref 的 dom 节点,然后将该节点作为第二个参数传递给 createPortal 函数?我想尽可能避免分配 ID。这是示例代码(我在 TypeScript 中工作):

export default React.forwardRef<HTMLDivElement, Props>( (Props, ref) =>{

        const {state, options, dispatch} = Props                     
        if(!state.open) return null
    
        return  ReactDOM.createPortal(
            <div
            className={css`
              height: 140px;
              background: white;
              overflow-y: scroll;
              position: absolute;
              width:100%;
            `}
          >
            {options}
          </div>,
          ref.current // <-- THIS DOESN'T WORK
        )
        
    }    
)

标签: reactjsreact-portal

解决方案


forwardRef当父组件需要访问子元素时使用,而不是相反。要将元素向下传递给孩子,您将通过道具来完成。此外,由于在第一次渲染之后才会填充 refs,因此您可能需要渲染父组件两次,一次将容器放在页面上,然后再次将其传递给子组件,以便子组件可以创建门户。

const Parent = () => {
  const ref = useRef<HTMLDivElement>(null);
  const [element, setElement] = useState<HTMLDivElement | null>(null);
  useEffect(() => {
    // Force a rerender, so it can be passed to the child.
    // If this causes an unwanted flicker, use useLayoutEffect instead
    setElement(ref.current); 
  }, []);

  return (
    <div ref={ref}>
      {element && (
        <Child element={element} {/* insert other props here */} />
      )}
    </div>
  )
}

const Child = (props: Props) => {
  const { state, options, dispatch, element } = props;
  if (!state.open) {
    return null;
  }

  return ReactDOM.createPortal(
    <div
      className={css`
        height: 140px;
        background: white;
        overflow-y: scroll;
        position: absolute;
        width: 100%;
      `}
    >
      {options}
    </div>,
    element
  );
}

推荐阅读