Эффект использования при onClick

#reactjs #react-hooks

#reactjs #реагирующие крючки

Вопрос:

Я зацикливаюсь на том, как использовать эффекты вместе с логикой приложения.

Предположим, что этот компонент:

 import React, { useEffect, useState } from "react";

export default function App() {
  const [query, setQuery] = useState('');

  useEffect( () => {
    fetch('https://www.google.com?q=' query)
    .then(response => console.log(response))
  }); // depends on what?

  return (
    <div>
      <input onChange={e => setQuery(e.target.value)} value={query} />
      <button>Ask Google about {query}</button>
    </div>
  );
}
  

Я хочу, чтобы:

  • когда (и только) пользователь нажимает кнопку, выполняется выборка с правильным значением запроса входных данных
  • если выборка все еще выполняется, и пользователь нажимает, выборка пропускается, но эффект срабатывает (что означает: я намеренно не отключаю кнопку, я хочу, чтобы функция эффекта была запущена, но я поставил флажок внутри этой функции не выполнять выборку).

Проблемы:

  • Эффект не должен срабатывать при монтировании (это не имело бы никакого смысла)
  • Эффект не должен срабатывать при изменении запроса, но если я не помещаю переменную запроса в массив зависимостей useEffect, React жалуется (react-hooks / исчерпывающий-deps)
  • Эффект должен срабатывать, когда пользователь нажимает на кнопку; Я добился этого, например, используя поддельное состояние isRun , установив onClick={setIsRun(true)} , сделав эффект зависящим от [isRun] , установив setIsRun(false) в конце функции эффекта и проверив if (!isRun) в начале функции эффекта, чтобы предотвратить повторный запуск, когда для самого эффекта установлено значение false, поскольку состояние изменяется. Это работает, но я нахожу это очень подробным и неудобным…
  • Эффект должен сработать, если кнопка нажата снова (с тем же query значением или нет), и предыдущая выборка еще не завершена без запуска выборки: с предыдущим решением с isRun оно не сработало бы, потому что isRun уже установлено на 1 , поэтому нет изменения состояния; возможно, с другим состоянием есть способ, но опять же очень подробный и нелогичный.
  • Самое главное: код должен быть чистым и читаемым без использования «трюков»!

Как бы вы написали такой компонент?

Ответ №1:

Похоже, вам вообще не следует использовать useEffect для этого. Вы хотите, чтобы это происходило при пользовательском действии, а не как эффект:

когда (и только) пользователь нажимает кнопку, выполняется выборка с правильным значением запроса входных данных

Удалите useEffect и создайте функцию для обработки щелчка:

 const handleClick = (e) => {
    fetch('https://www.google.com?q=' e.target.value)
        .then(response => console.log(response));
};
  

И передать эту функцию компоненту:

 <button onClick={handleClick}>Ask Google about {query}</button>
  

Что здесь кажется запутанным, так это эти требования:

  • если выборка все еще выполняется и пользователь нажимает, выборка пропускается, но эффект срабатывает
  • Эффект должен сработать, если кнопка нажата снова (с тем же значением запроса или нет), и предыдущая выборка еще не завершена без запуска выборки

Единственное, что делает функция, это выполняет fetch . Так должна ли эта операция произойти или нет? Предложенное вами решение сохранения состояния в переменной ( isRun ), чтобы определить, должно ли это произойти или нет, должно работать в этом случае. Я думаю, что проблема раньше заключалась в смешивании этого с useEffect , когда все, что вам действительно нужно, — это функция. Добавить isRun в состояние и соответствующим образом обновить его при выполнении операции:

 const [isRun, setIsRun] = useState(false);

const handleClick = (e) => {
    if (isRun) { return; }
    setIsRun(true);
    fetch('https://www.google.com?q=' e.target.value)
        .then(response => {
            console.log(response);
            setIsRun(false);
        });
};
  

Комментарии:

1. Я понимаю, теперь я еще больше запутался, чем раньше 🙂 Разве не было основного правила реакции, согласно которому все, что имеет дело с состоянием и реквизитами асинхронным способом, должно быть введено в действие, иначе вы не гарантируете, что данные будут прочитаны / записаны по назначению? Я использовал эффект в примере, поскольку я читаю состояние (запрос), и я мог бы также установить состояние после выборки, возможно, для передачи результата другому компоненту в качестве реквизита. Разве это не вариант использования useEffect?

2. @edoedoedo: useEffect предназначен для выполнения операции («побочный эффект») при монтировании компонента, условно основанной на изменениях в предоставленном массиве зависимостей. Но если вы не хотите, чтобы это происходило при монтировании компонента, и хотите, чтобы это происходило, когда пользователь нажимает кнопку, то useEffect это неправильный инструмент для работы. Обработка onClick — это то, как вы выполняете действие в ответ на щелчок пользователя.

3. Хорошо, спасибо, поэтому я понимаю, что совершенно неправильно понял, для чего нужны эффекты. Итак, для классического варианта использования, когда я извлекаю некоторые данные, когда пользователь что-то делает, а затем отображаю эти данные в компонентном рендеринге, совершенно безопасно использовать just function без необходимости использования эффектов, верно?

Ответ №2:

Я не уверен, хотите ли вы настаивать на использовании useEffect , но это не кажется подходящим для данной ситуации. Что я мог бы сделать, так это вызвать функцию при нажатии кнопки.

 import React, { useEffect, useState } from "react";

export default function App() {
  const [query, setQuery] = useState('');

  const handleQuery = (query) => {
    fetch('https://www.google.com?q=' query)
    .then(response => console.log(response))
  }

  return (
    <div>
      <input onChange={e => setQuery(e.target.value)} value={query} />
      <button onClick={() => handleQuery(query)}>Ask Google about {query}</button>
    </div>
  );
}
  

Комментарии:

1. На самом деле вам не нужно передавать запрос в функцию, потому что вы можете просто получить его из состояния