Получить значение useState внутри слушателя

Я хочу отобразить список координат, полученных в результате события щелчка по 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 еще раз. Как этого избежать?

попробуйте обернуть handleClick внутри хука useCallback с соответствующими зависимостями setClickCoord и координат. Это помогает?

Boris Borais 16.04.2024 19:06

@BorisBorais Я не уверен, что тебя понял, React.useCallback(()=> { }, [setClickCoord, координаты]), но если я помещу handleClick внутри(), это ничего не изменит

Matveev Aleksandr 16.04.2024 19:37

Кстати, я просто хочу подчеркнуть, что элементы div являются семантически неинтерактивными HTML-элементами. Если вы хотите сделать их интерактивными, добавьте соответствующую роль ARIA, индекс табуляции и обработчики нажатия/клавиши.... или, может быть, просто используйте элемент button и примените CSS. 🤷🏻‍♂️

Drew Reese 16.04.2024 19:47

Проблема, с которой вы столкнулись, заключается в устаревшем закрытии состояния coordinates в обработчике обратного вызова. Используйте обновление функционального состояния, чтобы функция обновления setClickCoord получила доступ к текущему состоянию и ваша проблема исчезла.

Drew Reese 16.04.2024 19:49

Кажется, вы все еще боретесь с проблемой закрытия Javascript. Можете ли вы отредактировать , включив в него более полный и репрезентативный минимально воспроизводимый пример реального кода, который вы пытаетесь использовать?

Drew Reese 16.04.2024 20:09
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
5
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вам следует избегать использования собственных прослушивателей событий 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).

первый фрагмент будет работать хорошо. Но во втором случае это может привести к сбою из-за устаревшей ссылки.

Boris Borais 16.04.2024 19:10
Ответ принят как подходящий

Почему бы не добавить обработчик кликов к элементу и сделать это в стиле 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] )

Matveev Aleksandr 16.04.2024 19:50

@MatveevAleksandr Сделайте эту проверку в обратном вызове setCoordinates, где у вас есть доступ к текущему значению состояния, а не за его пределами, например. setCoordinates(coordinates => condition ? nextCoordinates : coordinates). Кроме того, крючок useCallback здесь вам не поможет.

Drew Reese 16.04.2024 19:51

@MatveevAleksandr Я обновил вызов setCoordinates, чтобы проверить значение prev на наличие значения clientX.

Mr. Polywhirl 16.04.2024 20:01

@DrewReese coordinates внутри setCoordinates(coordinates => condition ? nextCoordinates : coordinates) пустой массив

Matveev Aleksandr 16.04.2024 20:06

@MatveevAleksandr Он будет пустым, пока вы не обновите его до непустого значения.

Drew Reese 16.04.2024 20:07

@MatveevAleksandr, что не так с кодом выше? Он добавляет новую координату X в список при нажатии. После обновления списка координат существует отдельный эффект для отображения обновленного списка координат. Не пытайтесь получить доступ к фактическому состоянию coordinates при попытке вызвать setCoordinates. Это не метод React.

Mr. Polywhirl 16.04.2024 20:16

Другие вопросы по теме