#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. На самом деле вам не нужно передавать запрос в функцию, потому что вы можете просто получить его из состояния