next.js 仅当存在子项时,显示带有React Portal的工具栏

wa7juj8i  于 2023-04-20  发布在  React
关注(0)|答案(1)|浏览(129)

**目标:**只有#toolkitArea有子项时才显示ToolkitArea
**问题:**由于某些原因,我无法计算ToolkitArea中当前的子项数量
到目前为止我所做的:

我有一个组件“工具栏”,我希望它显示时,通过ReactPortal添加按钮。
我目前的方法是在toolkitArea中触发一个UseEffect,检查该区域中的children数量。

import {  faEyeSlash, faEye } from '@fortawesome/free-solid-svg-icons'
import ToolkitButton from './ToolkitButton'
import { useState } from 'react'
export interface ToolkitAreaProps {
  children?: React.ReactNode

}
const ToolkitArea = ({ children }:ToolkitAreaProps) => {
  const [visible, setVisible] = useState<boolean>(true)
  const [isEmpty, setEmpty] = useState<boolean>(false)
  const toolkit = useRef<HTMLDivElement>(null)
  useEffect(() => {
    const toolkitArea = (document.getElementById('toolkitArea') as HTMLDivElement)
    console.log(toolkitArea.children, '', toolkit.current?.children, toolkit.current?.children.length)
    if(toolkitArea.children.length > 0)
      setEmpty(false)
    else setEmpty(true)
  }, [toolkit])

  return <div className="fixed bottom-6 w-full h-16 z-40 px-8 md:pl-32 md:pr-16 flex">
    <div className={`-ml-4 md:-ml-12 rounded-md w-16 h-16 bg-indigo-700 flex items-center justify-center shadow shadow-black shadow-md ${(visible || isEmpty) ? 'invisible' : 'visible'}`}>
      <ToolkitButton icon={faEye} onClick={() => setVisible(true)} tooltip={'Show Toolbar'}/>
    </div>
    <div className={`h-full mx-auto bg-indigo-700 w-fit max-w-full rounded-md shadow shadow-black shadow-md py-2 flex px-4 overflow-x-auto md:overflow-x-visible ${(visible || isEmpty) ? 'invisible' : 'visible'}`}>
      <ToolkitButton icon={faEyeSlash} onClick={() => setVisible(false)} className="-order-1" tooltip={'Toggle Toolbar'}/>
      <div ref={toolkit} className={'w-fit flex gap-4 h-full pl-4'} id={'toolkitArea'}>

      </div>
    </div>
  </div>
} 

export default ToolkitArea

浏览器中的输出如下所示:

它说children.length为0,即使显示了5个元素。
我不知道是什么原因导致了这种行为。我只能想象,按钮是在useEffect触发器之后添加的,浏览器内部的console.log是经过调整的。
当toolkitArea中有子项时,我如何才能实现只显示整个toolkitArea的目标?

cnjp1d6j

cnjp1d6j1#

一般来说,当门户内容发生变化时,React不会重新渲染ToolkitArea组件,因此不会触发useEffect钩子。即使它触发了,您也可能无法依赖渲染顺序来准确查看#toolkitArea门户包含多少个子组件。
我建议以HTML5 MutationObserver的形式使用一些稍微重一点的机器,每当#toolkitArea div的内容发生变化时,在ToolkitArea中运行一个回调。
您可以将必要的逻辑 Package 在钩子中:

function useEmptyDetector(element: HTMLDivElement | null): boolean
{
  // Watch the given element and return true when it has no children

  const [isEmpty, setIsEmpty] = useState(!element || element.childElementCount === 0);

  useEffect(() => {
    if (element) {
      // Watch for changes to the child list, and update isEmpty accordingly
      const observer = new MutationObserver(() => {
        setIsEmpty(element.childElementCount === 0)
      });
      observer.observe(element, { childList: true });

      return () => observer.disconnect();  // cleanup when unmounted
    }
    else setIsEmpty(true);
  }, [element, setIsEmpty]);

  return isEmpty;
}

Sandbox
钩子返回一个值isEmpty,然后你可以在你的组件中使用它(注意#toolkitArea使用了一个回调ref而不是ref对象):

const ToolkitArea = ({ children }:ToolkitAreaProps) => {
  const [visible, setVisible] = useState<boolean>(true)
  const [toolkit, setToolkit] = useState<HTMLDivElement>();
  const isEmpty = useEmptyDetector(toolkit);

  return <div className="fixed bottom-6 w-full h-16 z-40 px-8 md:pl-32 md:pr-16 flex">
    <div className={`-ml-4 md:-ml-12 rounded-md w-16 h-16 bg-indigo-700 flex items-center justify-center shadow shadow-black shadow-md ${(visible || isEmpty) ? 'invisible' : 'visible'}`}>
      <ToolkitButton icon={faEye} onClick={() => setVisible(true)} tooltip={'Show Toolbar'}/>
    </div>
    <div className={`h-full mx-auto bg-indigo-700 w-fit max-w-full rounded-md shadow shadow-black shadow-md py-2 flex px-4 overflow-x-auto md:overflow-x-visible ${(visible || isEmpty) ? 'invisible' : 'visible'}`}>
      <ToolkitButton icon={faEyeSlash} onClick={() => setVisible(false)} className="-order-1" tooltip={'Toggle Toolbar'}/>
      <div ref={setToolkit} className={'w-fit flex gap-4 h-full pl-4'} id={'toolkitArea'}>

      </div>
    </div>
  </div>
}

相关问题