React useCallback 函数没有获取当前上下文值

React useCallback function doesn't get current context value

我正在尝试使用 React Hooks 和 React Context API 以及鼠标事件来实现一个用例。

我想为容器添加 mousemove 事件。如果用户移动到一个对象(矩形)上,则会调用调度操作并更新上下文值。我想通过在调度前检查上下文值来实现不重复调度操作。问题是函数没有获取当前上下文值。

这个事件函数useMouseEvents.js

import * as React from "react";

import { DragToCreateContext, actionTypes } from "./reducer";

export function useMouseEvents(elRef) {
  const { dragToCreate, dispatchDragToCreate } = React.useContext(
    DragToCreateContext
  );

  console.log("out of callback", dragToCreate);

  const handleMouseMove = React.useCallback(
    (evt) => {
      if (evt.target.tagName === "P") {
        console.log("inside callback", dragToCreate);
      }
      if (evt.target.tagName === "P" && dragToCreate.sourceNodeId === null) {
        console.log("dispatch");
        dispatchDragToCreate({
          type: actionTypes.ACTIVATE,
          sourceNodeId: 1
        });
      }
    },
    [dragToCreate]
  );

  React.useEffect(() => {
    const el = elRef?.current;
    if (el) {
      el.addEventListener("mousemove", handleMouseMove);
      return () => {
        el.addEventListener("mousemove", handleMouseMove);
      };
    }
  }, [elRef, handleMouseMove]);
}

codesandbox.io

如果将鼠标悬停在矩形上,您将在控制台日志中看到:

inside callback {sourceNodeId: null}
dispatch 
out of callback {sourceNodeId: 1}
inside callback {sourceNodeId: null}
dispatch 
out of callback {sourceNodeId: 1}
inside callback {sourceNodeId: 1}
inside callback {sourceNodeId: null}

但应该是

inside callback {sourceNodeId: null}
dispatch 
out of callback {sourceNodeId: 1}
inside callback {sourceNodeId: 1}

我真的找不到我希望得到的答案,但这里有一些东西给你,也许还有其他想写答案的人:

inside callback {sourceNodeId: null} 告诉我有什么东西导致 Context 重置为它的初始值,并且你使用 Provider 的方式与我通常看到的非常不典型(改变这个似乎并没有真正解决什么都可以)。

我认为 useMouseEvents 内部的 useContext 可能只是获取默认上下文,但我尝试移动一些东西以保证情况并非如此,但似乎没有用. (其他人可能想重试这个?)

编辑:删除了这条建议

有点与问题无关,但您也想更改 useEffect

  React.useEffect(() => {
    const el = elRef?.current;
    if (el) {
      el.addEventListener("mousemove", handleMouseMove);
      return () => {
        el.removeEventListener("mousemove", handleMouseMove);
      };
    }

您看到的行为是因为当您的上下文值发生变化时,您在 mouseMove 上的侦听器会被删除和添加。此外,由于您的侦听器是在 useEffect 中重新创建的,因此可能会发生在附加新侦听器之前,旧的侦听器会执行并且您会从闭包中获得旧值的情况。

要解决此类情况,您可以使用 ref 来跟踪更新的上下文值并在侦听器回调中使用它。这样你就可以避免添加和删除鼠标事件监听器

import * as React from "react";

import { DragToCreateContext, actionTypes } from "./reducer";

export function useMouseEvents(elRef) {
  const { dragToCreate, dispatchDragToCreate } = React.useContext(
    DragToCreateContext
  );

  console.log("out of callback", dragToCreate);
  const dragToCreateRef = React.useRef(dragToCreate);
  React.useEffect(() => {
    dragToCreateRef.current = dragToCreate;
  }, [dragToCreate]);

  const handleMouseMove = React.useCallback((evt) => {
    if (evt.target.tagName === "P") {
      console.log("inside callback", dragToCreateRef.current);
    }
    if (
      evt.target.tagName === "P" &&
      dragToCreateRef.current.sourceNodeId === null
    ) {
      console.log("dispatch");
      dispatchDragToCreate({
        type: actionTypes.ACTIVATE,
        sourceNodeId: 1
      });
    }
  }, []);

  React.useEffect(() => {
    const el = elRef?.current;
    if (el) {
      el.addEventListener("mousemove", handleMouseMove);
      return () => {
        el.addEventListener("mousemove", handleMouseMove);
      };
    }
  }, [elRef, handleMouseMove]);

}

Working codesandbox demo