You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React-Router路由跳转时函数组件Ref为空问题求助

Fixing the Null Ref Issue When Using React Router with a Dropdown Menu

Hey Nick, let's break down what's causing that frustrating null ref error and fix it up!

What's Going Wrong Here?

When you click the <Link> to navigate to the login page, here's the sequence of events that leads to the error:

  1. React Router catches the click via its synthetic event system and starts unmounting the Landing component (and its child LandingMenu).
  2. The native click event on the body fires next—since native events bubble up after React's synthetic events finish processing.
  3. By this point, the LandingMenu is already in the process of unmounting, so menuRef.current becomes null. When your onBodyClick listener tries to call contains() on null, you get the error.

Quick Fix: Guard Against Null Ref

The simplest way to stop the error is to add a check for menuRef.current before calling contains(). Use optional chaining (?.) to make this clean and concise:

const onBodyClick = (event) => {
  // Only proceed if the ref exists AND the target is inside the menu
  if (menuRef.current?.contains(event.target)) {
    return;
  }
  setOpen(false);
};

This skips the contains() call entirely if the ref is null, preventing the error.

More Robust Cleanup Logic

While the above fixes the immediate issue, we can also refine the useEffect to ensure the event listener is always cleaned up properly—even if the component unmounts mid-navigation:

useEffect(() => {
  const onBodyClick = (event) => {
    if (menuRef.current?.contains(event.target)) {
      return;
    }
    setOpen(false);
  };

  // Add the listener only when the menu is open
  if (open) {
    document.body.addEventListener("click", onBodyClick);
  }

  // Always return a cleanup function, regardless of whether we added the listener
  return () => {
    document.body.removeEventListener("click", onBodyClick);
  };
}, [open]);

This ensures no leftover event handlers hang around if the component unmounts, keeping your app clean and performant.

Do You Need to Switch to a Class Component?

Absolutely not! Function components with useEffect are fully capable of handling this scenario. The problem wasn't missing cleanup on unmount (your original logic works when the menu closes normally)—it was just the timing of the native click event firing after the component starts unmounting.

Full Updated LandingMenu Code

Here's the complete fixed LandingMenu component with both improvements:

import React, { useState, useEffect, useRef } from "react";
import "../../styles/landing-menu.css";

const LandingMenu = ({ Title, Icon, children }) => {
  const [open, setOpen] = useState(false);
  const menuRef = useRef();

  useEffect(() => {
    const onBodyClick = (event) => {
      // Guard against null ref to avoid errors
      if (menuRef.current?.contains(event.target)) {
        return;
      }
      setOpen(false);
    };

    if (open) {
      document.body.addEventListener("click", onBodyClick);
    }

    // Ensure listener is always cleaned up
    return () => {
      document.body.removeEventListener("click", onBodyClick);
    };
  }, [open]);

  return (
    <nav className="landing-menu" ref={menuRef}>
      <div 
        className="landing-menu-title-wrapper" 
        onClick={() => setOpen(!open)}
      >
        <h3 className="landing-menu-title">{Title}</h3>
        <div className="landing-menu-title-icon">{Icon}</div>
      </div>
      <ul className={`dropdown ${open ? "visible" : "hidden"}`}>
        {children}
      </ul>
    </nav>
  );
};

export default LandingMenu;

These small tweaks will resolve the null ref error while keeping your dropdown's intended behavior intact—no need to overhaul your React Router setup or component structure!

内容的提问来源于stack exchange,提问作者NickC

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 20:07:50