Итак, согласно документации React, исходное состояние приведенного ниже кода canMove истинно. Я изменил его на ложь.
import React from "react";
import "./style.css";
import { useState, useEffect } from 'react';
export default function App() {
const [position, setPosition] = useState({ x: 0, y: 0 });
const [canMove, setCanMove] = useState(false);
useEffect(() => {
function handleMove(e) {
setPosition({ x: e.clientX, y: e.clientY });
}
if (canMove)
window.addEventListener('pointermove', handleMove);
return () => window.removeEventListener('pointermove', handleMove);
}, [canMove]);
return (
<>
<label>
<input type = "checkbox"
checked = {canMove}
onChange = {(e) => {
setCanMove(e.target.checked)
}
}
/>
The dot is allowed to move
</label>
<hr />
<div style = {{
position: 'absolute',
backgroundColor: 'pink',
borderRadius: '50%',
opacity: 0.6,
transform: `translate(${position.x}px, ${position.y}px)`,
pointerEvents: 'none',
left: -20,
top: -20,
width: 40,
height: 40,
}} />
</>
);
}
Таким образом, после первого рендеринга useEffect
запускается, но добавление прослушивателя событий в "pointermove"
зависит от canMove
, что теперь является ложным, и, следовательно, прослушиватель событий не добавляется. Теперь, если мы установим флажок canMove
true, это снова активирует useEffect
, и сначала должна запуститься очистка. Но ранее не было добавлено функции handlemove
для перемещения указателя. Значит, эта строка логически должна выдавать ошибку?
Постановка в очередь удаления прослушивателя событий не является ошибкой... если он существует, он будет удален, в противном случае он игнорируется. См. EventTarget: RemoveEventListener:
Вызов
removeEventListener()
с неидентифицирующими аргументами любой зарегистрированный в настоящее время прослушиватель событий наEventTarget
не имеет эффект.
Это означает, что ваш текущий/исходный код в порядке, ошибка не должна возникать, поскольку прослушиватель событий не был добавлен ранее.
Когда canMove меняется на false, выполняется первое условие, новый handleMove все еще не определен, а раньше handleMove уже добавлен в pointermove, так почему его удаление приводит к ошибке.
@Индра Извините, вы видите какую-то ошибку? В своем посте вы сказали, что его нет, но по какой-то причине вы его ждали. Однако в своем первом комментарии вы пытаетесь ссылаться на handleMove
до того, как он будет объявлен, и на самом деле он объявлен в другой области JavaScript, то есть в области действия if-блока. Вероятно, это сработало бы, если бы вы переместили объявление handleMove
в начало функции обратного вызова useEffect
, как в вашем посте.
Да, в моем первом комментарии. Я только что удалил очистку и добавил обе в настройку useEffect. Моя идея заключалась в том, что если handleMove добавляется к прослушивателю событий, когда useEffect запускается изначально, а затем canMove становится ложным, тогда он переходит к первому блоку if и сталкивается с необходимостью удалить handleMove, который уже добавлен в pointermove. Я ожидаю, что это сработает, поскольку, когда canMove истинно, он не переходит в блок if, где определен handleMove. И я прошу JS просто удалить функцию handleMove, которая была добавлена ранее.
@Indra Эта версия ошибочна, поскольку теперь вы опускаете функцию очистки, чтобы удалить прослушиватель событий при размонтировании компонента и перед применением каких-либо новых эффектов. Я не думаю, что есть какие-либо проблемы с кодом, который у вас есть в вашем сообщении. Вы имеете в виду, что у вас есть какие-то проблемы с этим или он работает не так, как вы ожидаете? Он добавляет прослушиватель событий, когда canMove
имеет значение true, и возвращает функцию очистки для удаления любого добавленного прослушивателя.
нет, я имею в виду не код в вопросе, а это
@Индра Хорошо, это проблема, которую я описал, на которую вы пытаетесь ссылаться handleMove
, прежде чем она будет объявлена. Перемещение handleMove
вверх устраняет эту проблему, но удаление по-прежнему не работает по другой причине, которую я описал. Тем не менее, когда я редактирую эту песочницу в соответствии с кодом, который есть в вашем сообщении, пользовательский интерфейс работает так, как я думаю, вы и намереваетесь. Когда флажок «можно перемещать» снят, точка прилипает и не перемещается, а когда флажок установлен, точка следует за мышью.
Но перемещение handleMove вверх совершенно не решает проблему. Можете ли вы поделиться ссылкой.
@Индра, я не очень знаком со stackblitz, но вот ваш отредактированный код в работающем кодах и ящике. Однако, как я уже сказал, перемещение handleMove
в начало обратного вызова эффекта устраняет только проблему со ссылкой, но все еще остается проблема очистки эффекта для удаления всех ранее добавленных прослушивателей, которая не работает. Использование правильной возвращаемой функции очистки устраняет такие проблемы, например. код в вашем посте. Я неправильно понимаю ваш пост/вопрос?
Понятно. Но предположим, что очистка отсутствует: (В моем коде SB) -изначально canMove имеет значение true => прослушиватель handleMove добавлен в poinetermove. - для последующих рендеров (только из-за setPosition) код useEffect не запускается. -теперь canMove меняется на false => нет очистки в коде => установка запускается => если блок, когда canMove имеет значение false, запускается => handleMove все еще не определен в этом рендеринге (поскольку элемент управления не достигает этого конкретного блока if) => сейчас мы пытаемся удалить существующий добавленный handleMove. - почему это не работает?
Давайте продолжим обсуждение в чате.
Я ответил на твой последний чат
@Индра В чате? Кажется, у меня последний комментарий в чате.
так почему этот фрагмент кода не работает. `const [canMove, setCanMove] = useState(true); useEffect(() => { if (!canMove) window.removeEventListener('pointermove', handleMove); if (canMove){ function handleMove(e) { setPosition({ x: e.clientX, y: e.clientY }) ; } window.addEventListener('pointermove', handleMove } }, [canMove]); `остальная часть кода такая же. Здесь useEffect запускается и добавляет handleMove к pointermove.