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

React中Ref.current为null报错问题排查与修复求助

解决React组件中ref.current为null的点击事件报错问题

问题描述

  • 初始阶段:Dropdown组件运行正常,可正常打开和关闭;
  • 切换到Search组件并点击后,触发“wrapper.current is null”报错;
  • 返回Dropdown页面并点击组件时,Dropdown和Search组件均出现大量报错;
  • 刷新页面后,组件暂时恢复正常,但切换到其他组件后问题会再次出现。

报错原因分析

  1. 重复绑定且未清理全局事件:Search组件在handleClick中每次点击都给document添加新的点击事件,且从未移除。组件卸载后这些事件依然存在,触发时会访问已销毁组件的ref,导致ref.current为null。
  2. 未处理组件卸载后的事件触发:Dropdown组件的useEffect中添加了全局点击事件,但没有清理函数。组件卸载后事件触发时,dropdownWrapper.current或dropdown.current已不存在,直接调用contains会报错。
  3. 未先判断ref状态就调用方法:直接访问ref.current.contains,未先检查ref.current是否存在,一旦ref未挂载或组件已卸载,就会抛出错误。

解决方案

  1. 所有全局事件绑定都要配套清理函数,组件卸载时移除事件监听,避免内存泄漏和无效引用;
  2. 调用ref.current的方法前,先判断ref.current是否存在;
  3. Search组件的全局点击事件改用useEffect绑定,避免重复添加事件。

修改后的代码

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

export default function Dropdown(props) {
  const [isActive, setIsActive] = useState(false);
  const dropdownWrapper = useRef(null);
  const dropdown = useRef(null);

  const handleClick = (event) => {
    setIsActive(true);
  };

  useEffect(() => {
    const handleDocumentClick = (event) => {
      // 先判断ref是否存在,再调用contains
      if (
        dropdownWrapper.current && dropdown.current &&
        !dropdownWrapper.current.contains(event.target) &&
        !dropdown.current.contains(event.target)
      ) {
        setIsActive(false);
      }
    };

    document.addEventListener("click", handleDocumentClick);
    
    // 添加清理函数,组件卸载时移除事件监听
    return () => {
      document.removeEventListener("click", handleDocumentClick);
    };
  }, []);

  return (
    <>
      <div
        ref={dropdownWrapper}
        onClick={handleClick}
        className="relative flex w-full cursor-pointer"
      >
        {props.children} {props.content}
        <div
          ref={dropdown}
          className={`${!isActive ? "hidden" : ""} 
          absolute -left-0 top-full z-30 mb-[4px] flex w-full flex-col`}
        >
          <div
            className={`scroll flex max-h-[310px] min-w-max flex-col overflow-hidden overflow-y-auto rounded-b-[8px] bg-blue-50 py-[8px] shadow-mm-1 dark:bg-gray-800 ${props.apart && "mt-[4px] rounded-[8px]"}`}
          >
            {props.menu}
          </div>
        </div>
      </div>
    </>
  );
}

export { Dropdown };

Search组件

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

export default function Search(props) {
  const [isActive, setIsActive] = useState(false);
  const wrapper = useRef(null);
  const result = useRef(null);

  const handleClick = (event) => {
    setIsActive(true);
  };

  useEffect(() => {
    const handleDocumentClick = (event) => {
      // 先判断ref是否存在,再调用contains
      if (
        wrapper.current && result.current &&
        !wrapper.current.contains(event.target) &&
        !result.current.contains(event.target)
      ) {
        setIsActive(false);
      }
    };

    document.addEventListener("click", handleDocumentClick);
    
    // 添加清理函数,组件卸载时移除事件监听
    return () => {
      document.removeEventListener("click", handleDocumentClick);
    };
  }, []);

  return (
    <>
      <div
        ref={wrapper}
        onClick={handleClick}
        className={`relative flex w-full min-w-[360px] max-w-[720px] cursor-pointer ${isActive ? "rounded-t-[32px]" : "rounded-full"}  bg-gray-800 `}
      >
        {props.children}
        <div
          ref={result}
          className={`${!isActive ? "hidden" : ""} 
          absolute -left-0 top-full z-30 mb-[4px] flex w-full flex-col `}
        >
          <div
            className={`flex max-h-[310px] min-w-max flex-col overflow-hidden overflow-y-auto rounded-b-[32px] bg-blue-50 py-[8px] shadow-mm-1 dark:bg-gray-800`}
          >
            {props.result}
          </div>
        </div>
      </div>
    </>
  );
}

export { Search };

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 08:10:25