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

React自定义Hook返回null且useEffect未触发问题排查

React自定义Hook useMouseSVGLocation测试失败排查

问题概述

我实现了一个用于转换SVG视口坐标的自定义Hook useMouseSVGLocation,但编写单元测试时发现Hook始终返回初始的null值,useEffect似乎未执行,导致测试断言失败。

自定义Hook代码

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

export default function useMouseSVGLocation(
  { clientX, clientY },
  [left, top],
  [canvas_height]
) {
  const [svgX, setSvgX] = useState(null);
  const [svgY, setSvgY] = useState(null);

  useEffect(() => {
    setSvgX(clientX - left);
    setSvgY(canvas_height - (clientY - top));
    console.log(`${clientX - left}`);
  });
  return [svgX, svgY];
}

单元测试代码

import useMouseSVGLocation from "../useMouseSVGLocation";
import React from "react";
import Enzyme, { shallow } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import toJson from "enzyme-to-json";

Enzyme.configure({ adapter: new Adapter() });

function TestComponent() {
  const [svgX, svgY] = useMouseSVGLocation(
    { clientX: 100, clientY: 50 },
    [100, 200],
    [200]
  );
  return (
    <div>
      {svgX}, {svgY}
    </div>
  );
}

describe("test useMouseSVGLocation custom hook", () => {
  it("test it in a function component", () => {
    let wrapper = shallow(<TestComponent></TestComponent>);
    wrapper.update();
    expect(toJson(wrapper)).toMatchSnapshot();
    expect(wrapper.find("div").text()).toEqual("0, 350");
  });
});

错误测试结果

● test useMouseSVGLocation custom hook › test it in a function component

expect(received).toEqual(expected) // deep equality

Expected: "0, 350"
Received: ", "

快照内容:

// Jest Snapshot v1, ...

exports[test useMouseSVGLocation custom hook test it in a function component 1] = <div> , </div>;

问题原因及解决方案

原因1:useEffect缺少依赖数组

原Hook中的useEffect未指定依赖数组,这会导致它在每次组件渲染时都执行,但在测试环境中,这种无依赖的副作用可能无法被正确触发或捕获,同时也违反React Hook规范,会触发ESLint依赖缺失警告。

原因2:Shallow渲染对Hook副作用支持有限

Enzyme的shallow渲染仅渲染组件本身,不会完整模拟React的生命周期副作用(比如useEffect的执行),因此无法捕获状态更新后的组件渲染结果。

修复方案

  1. 给useEffect添加正确的依赖数组
    修改Hook中的useEffect,将所有用到的外部变量加入依赖数组,确保副作用仅在依赖变化时执行:

    useEffect(() => {
      setSvgX(clientX - left);
      setSvgY(canvas_height - (clientY - top));
      console.log(`${clientX - left}`);
    }, [clientX, clientY, left, top, canvas_height]); // 添加依赖数组
    
  2. 改用mount渲染替代shallow
    在测试中使用mount完整模拟组件生命周期,确保useEffect被执行:

    // 导入mount
    import Enzyme, { mount } from "enzyme";
    
    // 修改测试代码中的渲染方式
    let wrapper = mount(<TestComponent></TestComponent>);
    
  3. 用act包裹渲染确保副作用完成
    用react-dom/test-utils中的act包裹组件渲染,确保所有副作用执行完成后再进行断言:

    import { act } from "react-dom/test-utils";
    
    // 修改测试用例
    it("test it in a function component", () => {
      let wrapper;
      act(() => {
        wrapper = mount(<TestComponent></TestComponent>);
      });
      wrapper.update();
      expect(toJson(wrapper)).toMatchSnapshot();
      expect(wrapper.find("div").text()).toEqual("0, 350");
    });
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 20:12:49