React TypeError: Cannot read property 'getBoundingClientRect' of undefined

React TypeError: Cannot read property 'getBoundingClientRect' of undefined

服务器 : Node.js, MongoDB, Graphql
前端:React --typescript、apollo-client、Graphql

我认为带来数据并呈现它是时间或顺序的问题。然而,我找不到解决这个问题的方法。

错误


  1. 未捕获类型错误:无法读取未定义的 属性 'getBoundingClientRect'。
  2. 未捕获(承诺)类型错误:无法读取未定义的 属性 'getBoundingClientRect'。

代码


import React, { MutableRefObject, useCallback, useLayoutEffect, useRef, useState } from 'react';
import { gql, useQuery } from '@apollo/client';
import { MainSlide } from '../../components';
import { MSlidersItf, MSlideItf } from '../../types';
import * as S from './MainSliderStyle';
import { GetWindowWidth } from '../../api/window-width/WindowWidth';

const GET_MAINSLIDERS = gql`
  query {
    getMainSliders {
      idx
      title {
        en
        ko
      }
      text {
        en
        ko
      }
      url
      src
      postingDate
    }
  }
`;

const SlideMapForm = (item: MSlideItf, i: number, Count: number) => {
  return (
    <MainSlide
      key={String(i + Count)}
      title={item.title}
      text={item.text}
      url={item.url}
      src={item.src}
      postingDate={item.postingDate}
    />
  );
};
export default function TestBtn() {
  const { loading, error, data } = useQuery<MSlidersItf>(GET_MAINSLIDERS);
  const MSlidersLength = data && data?.getMainSliders.length;
  const TotalSlideCount = MSlidersLength! + 4;

  // Ref
  const slidersRef = useRef() as MutableRefObject<HTMLDivElement>;

  // getSize
  const windowWidth: number = GetWindowWidth(); // Browser window width
  const [elWidth, setElWidth] = useState(0);
ERROR----------------------------------------------------------------------------------------
  const getEleWidth = useCallback(() => {
    if (data) {
      const { width } = slidersRef.current.children[2].getBoundingClientRect();
      setElWidth(width + 16); // Add Slide Margin
    }
  }, [data]);
ERROR----------------------------------------------------------------------------------------
  const sliderMargin: number = (windowWidth - elWidth) / 2;

  // Slide to Center
  const [center, setCenter] = useState(2);
  const sliderPosition: number = elWidth * center - sliderMargin; // Slide Position Inital Value

  // sliderControll
  const prevButton = () => setCenter(center - 1);
  const nextButton = () => setCenter(center + 1);

  useLayoutEffect(() => {
ERROR----------------------------------------------------------------------------------------
    getEleWidth();
ERROR----------------------------------------------------------------------------------------
    const resizeLitener = () => {
      const timing = setTimeout(() => getEleWidth(), 150);
      clearTimeout(timing);
    };

    window.addEventListener('resize', resizeLitener);
    window.addEventListener('load', resizeLitener);

    return () => {
      window.removeEventListener('resize', resizeLitener);
      window.removeEventListener('load', resizeLitener);
    };
  }, [TotalSlideCount, center, data, elWidth, getEleWidth, sliderPosition]);

  if (loading) return <h1>Loding...</h1>;
  if (error) return console.log(`MainSliders DB Error ${error}`);
  return (
    <S.Wrap>
      {data && (
        <S.SlidersArea ref={slidersRef} position={sliderPosition}>
          {Object.values(data.getMainSliders)
            .slice(-2)
            .map((item, i) => SlideMapForm(item, i, -MSlidersLength!))}
          {Object.values(data.getMainSliders).map((item, i) => SlideMapForm(item, i, 0))}
          {Object.values(data.getMainSliders)
            .slice(-2)
            .map((item, i) => SlideMapForm(item, i, MSlidersLength!))}
        </S.SlidersArea>
      )}
      <S.Arrows onClick={prevButton}>
        <S.Prev>
          <img src="/img/icon/arrow-next.svg" alt="Previous" />
        </S.Prev>
        <S.Next onClick={nextButton}>
          <img src="/img/icon/arrow-next.svg" alt="Next" />
        </S.Next>
      </S.Arrows>
    </S.Wrap>
  );
}

试试这个:

const getEleWidth = useCallback(() => {
  if (data) {
    const defaultValue = { width: 0 };
    const { width } = slidersRef.current?.children?[2]?.getBoundingClientRect?.() || defaultValue; // just check if the element you want exist 
    setElWidth(width + 16);
  }
}, [data, slidersRef.current]); //<=== add slidersRef to your dependencies

除了检查对象是否存在(如 Taghi Khavari 提到的那样),您可以尝试给它一个超时,以便您可以正确呈现 dom 元素。 它从来都不是最好的解决方案,但它可以让您的脚本快速运行。

(打算使用 Taghi 的解决方案)

const defaultValue = { width: 0 };
setTimeout( () => {
const { width } = slidersRef.current?.children?[2]?.getBoundingClientRect?.() || defaultValue; // just check if the element you want exist 
setElWidth(width + 16);}, 20) // You can change the number of milliseconds depending on your rendering