Я хочу отобразить список координат, полученных в результате события щелчка по DIV. Я хочу, чтобы прослушиватель работал только при нажатии кнопки.
import { createRoot } from "react-dom/client";
import React, {useCallback, useEffect, useState} from 'react'
const AppRestoration = () => {
const [active, setActive] = useState(false);
const [coordinates, setCoordinates] = useState([]);
const handleClick = useCallback((event) => {
console.info(coordinates) // is empty array
setCoordinates((prevCoordinates) => [...prevCoordinates, event.clientX]
);
}, [setCoordinates]);
useEffect(() => {
console.info("Coordinates:", ...coordinates); // When they update
}, [coordinates]);
return (
<div>
<div
style = {{ width: "200px", height: "200px", backgroundColor: "red" }}
onClick = {handleClick}
>
ici
</div>
<button onClick = {() => setActive((prev) => !prev)}>
{active ? "clicked" : "not"}
</button>
<SecondComponent secondCParam = {coordinates} />
</div>
);
};
const SecondComponent = ({ secondCParam }) => {
return (
<ul>
{secondCParam.map((coord) => (
<li key = {crypto.randomUUID()}>{coord}</li>
))}
</ul>
);
};
createRoot(document.getElementById("root")).render(<AppRestoration />)
Проблема в том, что в handleClick(event) я не могу фильтровать «координаты», это всегда пустой массив. Чтобы обновить его, мне нужно нажать на кнопку, поэтому handleClick еще раз. Как этого избежать?
@BorisBorais Я не уверен, что тебя понял, React.useCallback(()=> { }, [setClickCoord, координаты]), но если я помещу handleClick внутри(), это ничего не изменит
Кстати, я просто хочу подчеркнуть, что элементы div
являются семантически неинтерактивными HTML-элементами. Если вы хотите сделать их интерактивными, добавьте соответствующую роль ARIA, индекс табуляции и обработчики нажатия/клавиши.... или, может быть, просто используйте элемент button
и примените CSS. 🤷🏻♂️
Проблема, с которой вы столкнулись, заключается в устаревшем закрытии состояния coordinates
в обработчике обратного вызова. Используйте обновление функционального состояния, чтобы функция обновления setClickCoord
получила доступ к текущему состоянию и ваша проблема исчезла.
Кажется, вы все еще боретесь с проблемой закрытия Javascript. Можете ли вы отредактировать , включив в него более полный и репрезентативный минимально воспроизводимый пример реального кода, который вы пытаетесь использовать?
Вам следует избегать использования собственных прослушивателей событий JS в React. Вот как вы можете реорганизовать свой код для достижения того же результата с помощью подхода React:
export function AppRestoration() {
const [state, setState] = useState(false);
const [coordinates, setClickCoord] = useState<any[]>([]);
return (
<div>
<div id = "alex" onClick = {() => {
if (state) setClickCoord(prevCoordinates => [...prevCoordinates, event.clientX]);
}} style = {{ width: '200px', height: '200px', backgroundColor:'red' }}>ici</div>
<button onClick = {() => setState(!state)}>{state ? 'clicked' : 'not'}</button>
<SecondComponent
secondCParam = {coordinates} />
</div>
)
}
Однако, если вы хотите поэкспериментировать с прослушивателями событий и все равно использовать этот шаблон, вы можете провести рефакторинг handleClick
следующим образом:
const handleClick = (event) => {
// I need to get access to coordinates here (to filter them), but it is always an empty array
setClickCoord(prevCoordinates => {
console.info(prevCoordinates);
return [...prevCoordinates, event.clientX]);
});
}
Поскольку список обновляется, но console.info
ничего не показывает, проблема может заключаться в том, что ваш console.info
отображает только начальное значение координат ([]
).
Я настоятельно советую вам не использовать прослушиватели событий в React и использовать ссылки, когда вам нужно взаимодействовать с элементами DOM (вместо использования getElementById
или querySelector
).
первый фрагмент будет работать хорошо. Но во втором случае это может привести к сбою из-за устаревшей ссылки.
Почему бы не добавить обработчик кликов к элементу и сделать это в стиле React?
<div
style = {{ width: "200px", height: "200px", backgroundColor: "red" }}
onClick = {handleClick}
>
const handleClick = useCallback(({ clientX }) => {
setCoordinates((prevCoordinates) =>
!prevCoordinates.includes(clientX)
? [...prevCoordinates, clientX]
: prevCoordinates
);
}, [setCoordinates]);
Кроме того, состояние под названием state
неоднозначно. Вам следует переименовать его в более подходящее имя. Пока что я назвал его active
, так как кнопка переключается.
const { useCallback, useEffect, useState } = React;
const AppRestoration = () => {
const [active, setActive] = useState(false);
const [coordinates, setCoordinates] = useState([]);
const handleClick = useCallback((event) => {
setCoordinates((prevCoordinates) =>
!prevCoordinates.includes(event.clientX)
? [...prevCoordinates, event.clientX]
: prevCoordinates
);
}, [setCoordinates]);
useEffect(() => {
console.info("Coordinates:", ...coordinates); // When they update
}, [coordinates]);
return (
<div>
<div
style = {{ width: "200px", height: "200px", backgroundColor: "red" }}
onClick = {handleClick}
>
ici
</div>
<button onClick = {() => setActive((prev) => !prev)}>
{active ? "clicked" : "not"}
</button>
<SecondComponent secondCParam = {coordinates} />
</div>
);
};
const SecondComponent = ({ secondCParam }) => {
return (
<ul>
{secondCParam.map((coord) => (
<li key = {crypto.randomUUID()}>{coord}</li>
))}
</ul>
);
};
ReactDOM.createRoot(document.getElementById("root")).render(<AppRestoration />);
.as-console-wrapper { max-height: 4rem !important; }
<div id = "root"></div>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
спасибо за ваше решение, но прежде чем обновить мою coordinates
внутри handleClick
, я хочу проверить, есть ли эта координата уже в моих координатах. и если я это сделаю, мои координаты будут пустыми: const handleClick = useCallback( (event) => { if (coordinates.find(event.clientX)... // it gives an empty array coordinates setCoordinates((prevCoordinates) => [...prevCoordinates, event.clientX]); }, [setCoordinates] )
@MatveevAleksandr Сделайте эту проверку в обратном вызове setCoordinates
, где у вас есть доступ к текущему значению состояния, а не за его пределами, например. setCoordinates(coordinates => condition ? nextCoordinates : coordinates)
. Кроме того, крючок useCallback
здесь вам не поможет.
@MatveevAleksandr Я обновил вызов setCoordinates
, чтобы проверить значение prev
на наличие значения clientX
.
@DrewReese coordinates
внутри setCoordinates(coordinates => condition ? nextCoordinates : coordinates)
пустой массив
@MatveevAleksandr Он будет пустым, пока вы не обновите его до непустого значения.
@MatveevAleksandr, что не так с кодом выше? Он добавляет новую координату X в список при нажатии. После обновления списка координат существует отдельный эффект для отображения обновленного списка координат. Не пытайтесь получить доступ к фактическому состоянию coordinates
при попытке вызвать setCoordinates
. Это не метод React.
попробуйте обернуть handleClick внутри хука useCallback с соответствующими зависимостями setClickCoord и координат. Это помогает?