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]);
}
如果将鼠标悬停在矩形上,您将在控制台日志中看到:
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]);
}
我正在尝试使用 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]);
}
如果将鼠标悬停在矩形上,您将在控制台日志中看到:
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]);
}