在 React 功能组件中使用 ref 添加测试鼠标事件监听器

Posted

技术标签:

【中文标题】在 React 功能组件中使用 ref 添加测试鼠标事件监听器【英文标题】:Testing mouse event listener added using ref in React functional component 【发布时间】:2021-02-15 04:14:36 【问题描述】:

您好,我有一个如下所示的功能组件:

import React,  useRef, useEffect, useState  from 'react';

const SomeComponent = ( prop1, ...otherProps) => 
  const divRef = useRef();

  useEffect(() => 
    divRef.current.addEventListener('mousedown', mouseDownFunc);
  , []);

  const mouseDownFunc = () => 
    document.addEventListener('mousemove', (el) => 
        // call some parent function
    );
  

  return (
    <div
        className='test-div'
        ref= divRef >
    </div>
  );
;

如何测试一个反应功能组件,其中 addEventListener 是使用 useEffect 中的 ref 添加的,当触发时调用 mouseDownFunc。 我是新来的反应笑话测试,对如何做有点困惑。

【问题讨论】:

你尝试了什么?您使用 addEventListener 而不是 React 事件有什么具体原因吗? 【参考方案1】:

测试这种组件可能会很棘手,但使用@testing-library/react 我想我能够想出一些有用的东西。

我确实必须对您的组件进行一些更改以稍微公开 API,并且我还进行了一些更改,以便它停止侦听 mouseup 上的事件,这可能不是您想要的特定事件。

这是修改后的组件:

// MouseDownExample.js
import React,  useEffect, useState  from "react";

export default ( onMouseMoveWhileDown ) => 
  const [x, setX] = useState(null);
  const [listening, setListening] = useState();

  // Replaced with mouse move function, should make sure we're unlistening as well
  useEffect(() => 
    if (listening) 
      const onMouseMove = (event) => 
        // call some parent function
        onMouseMoveWhileDown(event);

        console.log(event.clientX);

        // purely for testing purposes
        setX(event.clientX);
      ;
      const onMouseUp = (event) => 
        // stop listening on mouse up
        // - you should pick whatever event you want to stop listening
        // - this is global so it also stops when the mouse is outside the box
        setListening(false);
      ;
      document.addEventListener("mousemove", onMouseMove);
      document.addEventListener("mouseup", onMouseUp);
      return () => 
        document.removeEventListener("mousemove", onMouseMove);
        document.removeEventListener("mouseup", onMouseUp);
      ;
    
  , [listening, onMouseMoveWhileDown]);

  return (
    <div
      style=
        backgroundColor: "red",
        width: 200,
        height: 200
      
      className="test-div"
      onMouseDown=() => 
        // moved this inline, so no ref
        setListening(true);
      
    >
      X Position: x
    </div>
  );
;

我在 cmets 中指出了主要区别。

这是一个示例测试:

// MouseDownExample.test.js
import React from "react";
import  fireEvent, render  from "@testing-library/react";
import MouseDownExample from "./MouseDownExample";

it("shouldn't trigger onMouseMoveWhileDown when mouse isn't down", () => 
  const onMouseMoveWhileDown = jest.fn();
  const  container  = render(
    <MouseDownExample onMouseMoveWhileDown=onMouseMoveWhileDown />
  );

  // Note: normally I would use `screen.getByRole` but divs don't have a useful role
  const subject = container.firstChild;

  fireEvent.mouseMove(
    document,
    // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent
    
      clientX: 200
    
  );
  // hasn't gone down yet
  expect(onMouseMoveWhileDown).not.toHaveBeenCalled();

  fireEvent.mouseDown(subject);
  fireEvent.mouseUp(subject);

  // went down then up before moving
  fireEvent.mouseMove(document, 
    clientX: 200
  );
  expect(onMouseMoveWhileDown).not.toHaveBeenCalled();
);

it("should trigger onMouseMoveWhileDown when mouse is down", () => 
  const onMouseMoveWhileDown = jest.fn();
  const  container  = render(
    <MouseDownExample onMouseMoveWhileDown=onMouseMoveWhileDown />
  );

  // Note: normally I would use `screen.getByRole` but divs don't have a useful role
  const subject = container.firstChild;

  fireEvent.mouseDown(subject);
  fireEvent.mouseMove(document, 
    clientX: 200
  );

  expect(onMouseMoveWhileDown).toHaveBeenCalledWith(
    expect.objectContaining( clientX: 200 )
  );
);

这里发生了什么,我们正在渲染组件,然后触发事件以确保在我们期望的时候调用 onMouseMoveWhileDown 函数属性。

我们必须做expect.objectContaining 而不仅仅是对象,因为它是用包含其他属性的MouseEvent 调用的。

我们可能要添加的另一个测试是卸载测试,以确保侦听器不再触发事件。

你可以用这个Code Sandbox 这个组件和测试来查看/实验。希望对你有帮助?

【讨论】:

如果您对所做的修改有任何问题,请 lmk 我们可以解决/尝试找出如何在不进行更改的情况下进行测试

以上是关于在 React 功能组件中使用 ref 添加测试鼠标事件监听器的主要内容,如果未能解决你的问题,请参考以下文章

组件挂载到 React 后如何创建 Refs?

使用 react-router-dom 时如何避免“功能组件不能被赋予 refs”?

React 功能组件 - 当组件返回元素数组时,如何将 refs 传递回父级?

React - 使用 refs 将焦点函数传递给子组件

处理用 TypeScript 编写的 React 功能组件中的 Ref:Element is not assignable 错误

浅渲染使用 Refs 的 React/Enzyme 组件