jaredLunde / react-hook

↩ Strongly typed, concurrent mode-safe React hooks
https://npmjs.com/org/react-hook
MIT License
1.44k stars 97 forks source link

Resize observer not firing #303

Open AdamKasela opened 1 year ago

AdamKasela commented 1 year ago
//useElementSize.tsx
import useResizeObserver from '@react-hook/resize-observer';
import { MutableRefObject, useLayoutEffect, useState } from 'react';

interface Size {
  width: number;
  height: number;
}

export default function useElementSize<T extends HTMLElement = HTMLDivElement>(
  target: MutableRefObject<T | null>,
) {
  const [size, setSize] = useState<Size>({
    width: 0,
    height: 0,
  });

  useLayoutEffect(() => {
    target.current && setSize(target.current.getBoundingClientRect());
  }, [target]);

  useResizeObserver(target, (entry) => {
    setSize(entry.contentRect);
  });

  return size;
}
//Other component
import { Box } from '@mui/material';
import clsx from 'clsx';
import { Fragment, useRef, useState } from 'react';
import { useInView } from 'react-intersection-observer';
import Config from '../../../../config';
import { ViewerMode } from '../../../../models';
import { ScannedPage } from '../../../../models/generated';
import useScrollNavigationStore from '../../../../stores/ScrollNavigationStore';
import style from './DocumentPage.module.scss';
import { Word } from '../../../ui';
import useSize from '@react-hook/size';
import useElementSize from '../../../../hooks/useElementSize';

type DocumentPageProps = {
  docIndex: number;
  docPageIndex: number;
  viewerMode: ViewerMode;
  scannedPage: ScannedPage;
  selected?: boolean;
};

export const DocumentPage = ({
  docIndex,
  docPageIndex,
  viewerMode,
  scannedPage,
  selected = false,
}: DocumentPageProps) => {
  const documentPageSrc = `${Config.API_BASE_URL}/api/v1/core/file/${encodeURIComponent(
    scannedPage.imagePath,
  )}/lob/Personenversicherung`;
  const [isLoading, setIsLoading] = useState(true);
  const setSelectedIndexes = useScrollNavigationStore(
    (state) => state.setSelectedIndexes,
  );
  const imgRef = useRef<HTMLImageElement>(null);
  const { width, height } = useElementSize<HTMLImageElement>(imgRef);
  const widthRatio = width / scannedPage.width;
  const heightRatio = height / scannedPage.height;
  const { ref, inView } = useInView({
    triggerOnce: true,
    rootMargin: '0px 0px 90% 0px',
    threshold: 0.8,
  });

  return (
    <Box
      ref={ref}
      component={'div'}
      data-inview={inView}
      alignItems={'center'}
      justifyContent={'center'}
      className={clsx(style.documentWrapper, {
        [style.selected]: selected,
        [style.loading]: isLoading,
      })}
      sx={{ minHeight: '200px' }}
      onClick={() => handleClick([docIndex, docPageIndex], viewerMode)}
    >
      {inView ? (
        <div style={{ position: 'relative' }}>
          <img
            ref={imgRef}
            loading="lazy"
            width={scannedPage.width}
            height={scannedPage.height}
            src={documentPageSrc}
            alt="document"
            placeholder="document"
            draggable={false}
            style={{ width: '100%', height: '100%', userSelect: 'none' }}
            onLoad={() => handleImgLoad()}
            aria-hidden="true"
          />
          {viewerMode === ViewerMode.Viewer &&
            scannedPage.scannedLineBoxes?.map((line, lineIndex) => {
              return (
                <Fragment key={`Line${lineIndex}`}>
                  {line.words?.map((word, wordIndex) => {
                    return (
                      <Box
                        key={`Line${lineIndex}-Word${wordIndex}`}
                        top={`${word.y * heightRatio}px`}
                        left={`${word.x * widthRatio}px`}
                        width={`${word.width * widthRatio}px`}
                        height={`${word.height * heightRatio}px`}
                        margin={0}
                        position={'absolute'}
                        color={'transparent'}
                        border={'1px solid red'}
                      >
                        <Word
                          height={`${word.height * heightRatio}px`}
                          lineHeight={`${word.height * heightRatio}px`}
                        >
                          {word.text}
                        </Word>
                      </Box>
                    );
                  })}
                </Fragment>
              );
            })}
        </div>
      ) : null}
    </Box>
  );
};

I'm not sure why but Resize observer is not triggering at all. I tried using useSize hook implemented from this lib but this also is not working. Can you pls help me somebody ? What can cause this issue ? When I use target.current in dependency array of useLayoutEffect the initial width and height get set but consecutive resizing doesn't trigger anything.

jaredLunde commented 2 months ago

Very straightforward: your img element enters and leaves the DOM and the hook doesn't use ref.current as a dependency, just ref. You need to set the ref via useState instead of useRef in this case, as shown in the example here: https://codesandbox.io/s/react-hookresize-observer-example-ft88x