簡體   English   中英

如何消除 React Hook 中 null 屬性的錯誤?

[英]How to rid error of null properties in react hook?

我正在嘗試自定義掛鈎以獲取少量數據。 我想檢查一個元素與第二個元素相比在哪里。 我想把它放在他們中間。 它有效,但我在控制台中重復出現錯誤。

錯誤:

未捕獲的類型錯誤:無法在 getPosition (usePosition.js:18:1) 讀取 null 的屬性(讀取“offsetLeft”)

import { useEffect, useRef, useState } from "react";

const usePosition = () => {
  const elRef = useRef();
  const elSecondRef = useRef();

  const [fromLeftEl, setFromLeftEl] = useState();
  const [fromTopEl, setFromTopEl] = useState();
  const [widthEl, setWidthEl] = useState();
  const [heightEl, setHeightEl] = useState();
  const [widthElSecond, setWidthElSecond] = useState();

  const [finalX, setFinalX] = useState();
  const [finalY, setFinalY] = useState();
  // This function calculates the position underneath the element and centering it with respect to the other element

  const getPosition = () => {
    const fromLeftEl = elRef.current.offsetLeft;
    setFromLeftEl(fromLeftEl);
    const fromTopEl = elRef.current.offsetTop;
    setFromTopEl(fromTopEl);
    const widthEl = elRef.current.offsetWidth;
    setWidthEl(widthEl);
    const heightEl = elRef.current.offsetHeight;
    setHeightEl(heightEl);
    const widthElSecond = elSecondRef.current.offsetWidth;
    setWidthElSecond(widthElSecond);
    const middleEl = widthEl / 2;
    const middleElSecond = widthElSecond / 2;
    const finalX = fromLeftEl + middleEl - middleElSecond;
    setFinalX(finalX);
    const finalY = fromTopEl + heightEl;
    setFinalY(finalY);
  };
  // Get the position of the first element
  useEffect(() => {
    getPosition();
  }, []);

  useEffect(() => {
    window.addEventListener("resize", getPosition);
    window.addEventListener("click", getPosition);
    window.addEventListener("scroll", getPosition);
    return () => window.removeEventListener("scroll", getPosition);
  }, []);

  return {
    elRef,
    elSecondRef,
    fromLeftEl,
    fromTopEl,
    widthEl,
    heightEl,
    finalY,
    finalX,
    widthElSecond,
  };
};
export default usePosition;

使用 usePosition 的組件:

import classNames from "classnames";
import { useState } from "react";
import useSticky from "./useSticky";
import usePosition from "./usePosition";

import "../style/sass/Nav.sass";
import { NavLink } from "react-router-dom";

const Nav = (props) => {
  const [isActive, setActive] = useState(false);
  const { sticky, stickyRef } = useSticky();
  const { elRef, elSecondRef, finalY, finalX } = usePosition();

  const handleToggle = () => {
    setActive(!isActive);
  };

  return (
    <div
      ref={stickyRef}
      className={classNames("nav", { sticky })}
      style={{
        display: props.display,
        height: sticky ? `${stickyRef.current?.clientHeight}px` : "10vh",
        width: props.width,
        gridTemplateColumns: props.navGridCol,
        gridTemplateRows: props.navGridRow,
        height: props.height,
        position: props.position,
        left: props.left,
        animation: props.animationMenu,
      }}
    >
      <div
        className="nav__logo"
        style={{
          gridArea: props.gArea1,
          height: props.heightHomeBtn,
          width: props.btnWidth,
          animation: props.animationHome,
          display: props.homeDisplay,
        }}
      >
        <NavLink className="link__logo" to="/">
          <i
            class="icon-home"
            style={{
              width: props.btnWidth,
              fontSize: props.fweight,
            }}
          ></i>
        </NavLink>
      </div>
      <NavLink
        className="link__about"
        to="/about"
        exact="true"
        style={{ gridArea: props.gArea2 }}
      >
        <button
          className="nav__aboutBtn"
          style={{
            height: props.btnHeight,
            width: props.btnWidth,
            animation: props.animationAbout,
            display: props.aboutDisplay,
          }}
        >
          <p>o firmie</p>
        </button>
      </NavLink>
      <button
        onClick={handleToggle}
        className={!isActive ? "offerDD__btn" : "offerDD__btn_active"}
        style={{
          gridArea: props.gArea3,
          height: props.btnHeight,
          width: props.btnWidth,
          animation: props.animationOffer,
          display: props.offerDisplay,
        }}
        ref={elRef}
      >
        <i class="icon-down-open"></i>
        <p>oferta</p>
      </button>
      <div
        className={
          !isActive ? "offerDD__dropdown_disabled" : "offerDD__dropdown"
        }
        ref={elSecondRef}
        style={{ top: `${finalY}px`, left: `${finalX}px` }}
      >
        <NavLink className="link__air" to="/air-conditioning">
          <div
            className="air__p"
            style={{ height: props.btnHeight, width: props.btnWidth }}
          >
            <p>klimatyzacje</p>
          </div>
        </NavLink>
        <NavLink className="link__vent" to="/ventilation">
          <div
            className="vent__p"
            style={{ height: props.btnHeight, width: props.btnWidth }}
          >
            <p>wentylacje</p>
          </div>
        </NavLink>
        <NavLink className="link__heat" to="/heat-pump">
          <div
            className="heat__p"
            style={{ height: props.btnHeight, width: props.btnWidth }}
          >
            <p>pompy ciepła</p>
          </div>
        </NavLink>
        <NavLink className="link__recu" to="/recuperation">
          <div
            className="recu__p"
            style={{ height: props.btnHeight, width: props.btnWidth }}
          >
            <p>rekuperacja</p>
          </div>
        </NavLink>
        <NavLink className="link__fire-protection" to="/fire-protection">
          <div
            className="fire__p"
            style={{ height: props.btnHeight, width: props.btnWidth }}
          >
            <p>ppoż</p>
          </div>
        </NavLink>
        <NavLink className="link__shop" to="/shop">
          <div
            className="shop__p"
            style={{ height: props.btnHeight, width: props.btnWidth }}
          >
            <p>sklep</p>
          </div>
        </NavLink>
      </div>
      <NavLink
        className="link__blog"
        to="/blog"
        style={{ gridArea: props.gArea4 }}
      >
        <button
          className="nav__blogBtn"
          style={{
            height: props.btnHeight,
            width: props.btnWidth,
            animation: props.animationBlog,
            display: props.blogDisplay,
          }}
        >
          <p>blog</p>
        </button>
      </NavLink>
      <NavLink
        className="link__contact"
        to="/contact"
        style={{ gridArea: props.gArea5 }}
      >
        <button
          className="nav__contactBtn"
          style={{
            height: props.btnHeight,
            width: props.btnWidth,
            animation: props.animationContact,
            display: props.contactDisplay,
          }}
        >
          <p>kontakt</p>
        </button>
      </NavLink>
      <NavLink
        className="link__login"
        to="/login"
        style={{ gridArea: props.gArea6 }}
      >
        <button
          className="nav__loginBtn"
          style={{
            height: props.btnHeight,
            width: props.btnWidth,
            animation: props.animationLogin,
            display: props.loginDisplay,
          }}
        >
          <p>zaloguj</p>
        </button>
      </NavLink>
    </div>
  );
};

export default Nav;

我想知道如何在控制台和工作掛鈎中正確編碼這個問題而不會出錯。

我知道這個錯誤是由於無法讀取不存在的東西而導致的,因此它是 null 但如何編寫此代碼以使其正確?

我沒有看到在消費組件中使用elRef的方式有任何明顯的問題。 elRef附加到 DOM 元素,例如button ,並且沒有條件渲染,因此button在初始渲染期間渲染到 DOM。

const Nav = (props) => {
  ...
  const { elRef, elSecondRef, finalY, finalX } = usePosition();

  ...

  return (
    <div
      ...
    >
      ...
      <button
        onClick={handleToggle}
        className={!isActive ? "offerDD__btn" : "offerDD__btn_active"}
        style={{
          gridArea: props.gArea3,
          height: props.btnHeight,
          width: props.btnWidth,
          animation: props.animationOffer,
          display: props.offerDisplay,
        }}
        ref={elRef}
      >
        <i class="icon-down-open"></i>
        <p>oferta</p>
      </button>
      ...
    </div>
  );
};

但是,我確實在usePosition掛鈎中看到了潛在的問題。 掛鈎不會清除它創建的所有效果。 在訪問潛在的 null 或未定義引用的情況下,建議在訪問屬性之前使用空檢查/保護子句或可選鏈接運算符。 在這種情況下,用於檢查 refs 是否具有真實當前值的if塊就足夠了。

const usePosition = () => {
  const elRef = useRef();
  const elSecondRef = useRef();

  const [fromLeftEl, setFromLeftEl] = useState();
  const [fromTopEl, setFromTopEl] = useState();
  const [widthEl, setWidthEl] = useState();
  const [heightEl, setHeightEl] = useState();
  const [widthElSecond, setWidthElSecond] = useState();

  const [finalX, setFinalX] = useState();
  const [finalY, setFinalY] = useState();

  // This function calculates the position underneath the element and centering it with respect to the other element
  const getPosition = () => {
    // Only access ref properties if there is a current value
    if (elRef.current && elSecondRef.current) {
      const fromLeftEl = elRef.current.offsetLeft;
      setFromLeftEl(fromLeftEl);
      const fromTopEl = elRef.current.offsetTop;
      setFromTopEl(fromTopEl);
      const widthEl = elRef.current.offsetWidth;
      setWidthEl(widthEl);
      const heightEl = elRef.current.offsetHeight;
      setHeightEl(heightEl);

      const widthElSecond = elSecondRef.current.offsetWidth;
      setWidthElSecond(widthElSecond);

      const middleEl = widthEl / 2;
      const middleElSecond = widthElSecond / 2;
      const finalX = fromLeftEl + middleEl - middleElSecond;
      setFinalX(finalX);

      const finalY = fromTopEl + heightEl;
      setFinalY(finalY);
    }
  };

  useEffect(() => {
    window.addEventListener("resize", getPosition, { passive: true });
    window.addEventListener("click", getPosition);
    window.addEventListener("scroll", getPosition, { passive: true });

    // Call once on initial component render
    getPosition();

    // Return cleanup function to remove all listener callbacks!
    return () => {
      window.removeEventListener("resize", getPosition, { passive: true });
      window.removeEventListener("click", getPosition);
      window.removeEventListener("scroll", getPosition, { passive: true });
    };
  }, []);

  return {
    elRef,
    elSecondRef,
    fromLeftEl,
    fromTopEl,
    widthEl,
    heightEl,
    finalY,
    finalX,
    widthElSecond,
  };
};

export default usePosition;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM