第一次点击 React Router Link 时 document.querySelector() 总是 return null,但之后会 return 正确

document.querySelector() always return null when clicking on React Router Link the first time but will return correctly after

我遇到了 React-Router 和 querySelector 的问题。

我有一个 Navbar 组件,其中包含用于导航的所有 CustomLink 组件和一个线条动画,它侦听这些组件并根据当前活动组件显示动画。

// Navbar.tsx

import React, { useCallback, useEffect, useState, useRef } from "react";
import { Link, useLocation } from "react-router-dom";

import CustomLink from "./Link";

const Layout: React.FC = ({ children }) => {
  const location = useLocation();
  const navbarRef = useRef<HTMLDivElement>(null);
  const [pos, setPos] = useState({ left: 0, width: 0 });

  const handleActiveLine = useCallback((): void => {
    if (navbarRef && navbarRef.current) {
      const activeNavbarLink = navbarRef.current.querySelector<HTMLElement>(
        ".tdp-navbar__item.active"
      );

      console.log(activeNavbarLink);

      if (activeNavbarLink) {
        setPos({
          left: activeNavbarLink.offsetLeft,
          width: activeNavbarLink.offsetWidth,
        });
      }
    }
  }, []);

  useEffect(() => {
    handleActiveLine();
  }, [location]);

  return (
    <>
      <div className="tdp-navbar-content shadow">
        <div ref={navbarRef} className="tdp-navbar">
          <div className="tdp-navbar__left">
            <p>Todo+</p>
            <CustomLink to="/">About</CustomLink>
            <CustomLink to="/login">Login</CustomLink>
          </div>
          <div className="tdp-navbar__right">
            <button className="tdp-button tdp-button--primary tdp-button--border">
              <div className="tdp-button__content">
                <Link to="/register">Register</Link>
              </div>
            </button>
            <button className="tdp-button tdp-button--primary tdp-button--default">
              <div className="tdp-button__content">
                <Link to="/login">Login</Link>
              </div>
            </button>
          </div>
          <div
            className="tdp-navbar__line"
            style={{ left: pos.left, width: pos.width }}
          />
        </div>
      </div>
      <main className="page">{children}</main>
    </>
  );
};

export default Layout;
// CustomLink.tsx
import React, { useEffect, useState } from "react";
import { useLocation, useHistory } from "react-router-dom";

interface Props {
  to: string;
}

const CustomLink: React.FC<Props> = ({ to, children }) => {
  const location = useLocation();
  const history = useHistory();
  const [active, setActive] = useState(false);

  useEffect(() => {
    if (location.pathname === to) {
      setActive(true);
    } else {
      setActive(false);
    }
  }, [location, to]);

  return (
    // eslint-disable-next-line react/button-has-type
    <button
      className={`tdp-navbar__item ${active ? "active" : ""}`}
      onClick={(): void => {
        history.push(to);
      }}
    >
      {children}
    </button>
  );
};

export default CustomLink;

但它并不如我所愿。所以我打开 Chrome Devtool 并调试,我意识到当我先点击一个 CustomLink 时, NavbarquerySelector() 将 return 为空。但是如果我多次点击同一个CustomLink,它会正常return,就像下面的截图:

Error from Chrome Console

我怎样才能第一次从 querySelector() 得到正确的 return?谢谢!

因为handleActiveLine会在CustomLink.tsxsetActive(true)之前触发

CustomLink.tsx中添加回调:

const CustomLink: React.FC<Props> = ({ onActive }) => {

  useEffect(() => {
    if (active) {
      onActive();
    }
  }, [active]);

}

Navbar.tsx中:

const Layout: React.FC = ({ children }) => {
  
  function handleOnActive() {
     // do your query selector here
  }

  // add onActive={handleOnActive} to each CustomLink
  return <CustomLink onActive={handleOnActive} />
}