首页 > 解决方案 > document.querySelector() 第一次点击 React Router Link 时总是返回 null 但之后会正确返回

问题描述

我遇到了 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第一个时,querySelector()fromNavbar会返回 null。CustomLink但是,如果我多次单击相同的内容,它将正确返回,如下面的屏幕截图:

来自 Chrome 控制台的错误

如何querySelector()从第一次获得正确的回报?谢谢!

标签: reactjsdomreact-router-domselectors-api

解决方案


这是因为会在之前handleActiveLine触发setActive(true)CustomLink.tsx

在 中添加回调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} />
}

推荐阅读