#javascript #reactjs #react-hooks #use-state
Вопрос:
Итак, я начал изучать React, выполняя компоненты класса. Там, насколько я понимал, что я делаю, если бы у меня был
this.state = { isLoading: false, dataToRender: [], } this.setState({ isLoading: true }, async () =gt; { const data = await fetchData(); this.setState({ dataToRender: data , isLoading: false }); }) const message =gt; lt;pgt;loading...lt;/pgt; if (isLoading) return message(); return ( dataToRender.map )
для отображения сообщения «загрузка» всякий раз, когда я вызываю API для получения новых данных, я мог бы знать, что «очередь» setState будет выполняться в определенном порядке. Внешнее состояние набора получит первое изменение состояния для первого параметра и функцию, которая будет выполняться сразу после него в качестве второго параметра.
Проблема в том, что мне немного сложно разобраться в способах достижения этой цели с помощью крючков. Я предполагаю, что делаю
const [isLoading, setIsLoading] = useState(false); const [data, setData] = useState([]); setLoading(async () =gt; { setLoading(true) const data = await fetchData() setData(data); return false; })
это бы не сработало, верно? Я чувствую, что общая логика построения такого рода компонентной логики может отличаться при использовании крючков каким-то образом… Если бы кто-нибудь мог хотя бы указать мне направление, было бы неплохо. Спасибо! 🙂
Ответ №1:
Функции, переданные как useState
функция обновления состояния, обрабатываются как синхронные функции, поэтому вы эффективно сохраняете неявно возвращенный объект Promise в состояние.
Что вам следует сделать, так это разложить асинхронную логику на функции , которые вызываются из обработчика событий, например onClick
, или вызываются в жизненном цикле компонента, например useEffect
.
const [isLoading, setIsLoading] = useState(false); const [data, setData] = useState([]); const asyncCall = async () =gt; { setLoading(true); try { const data = await fetchData(); setData(data); } catch(error) { // handle any errors, logging, etc... } finally { setLoading(false); } };
Это вызовет/поставит в очередь каждое обновление состояния в том порядке, в котором они поставлены в очередь. Вы также должны окружить любую асинхронную логику в try/catch
блоке, чтобы вы могли обрабатывать любые отклоненные обещания, очистить состояние загрузки в a finally
, чтобы, независимо от разрешенных/отклоненных обещаний, по завершении кода он сбрасывал состояние загрузки.
Комментарии:
1. Вау, это круто. Так чисто! Спасибо!
2. Привет, Дрю. Что касается вашего редактирования, в моем примере я посчитал, что блок try/catch был неявным в fetchData (), но это решение с finally интересно. Вопрос в следующем: какие ошибки могут возникнуть в setData? Я имею в виду, что как только я обработал ошибку в fetchData(), что может привести к (ошибке)?
3. @Zigoni О, я понимаю. Я занимался только синтаксисом, так
fetchData
как возвращает обещание, которого вы ждете, технически оно может вернуть отклоненное обещание. Я не думаю, что из функции обновления состояния React будут выдаваться какие-либо ошибки.4. На самом деле я рассматривал возможность возврата самой стоимости. Я должен признать, что я новичок и все же мало что знаю об обработке ошибок… Я предположил, что функция fetchData() будет содержать весь блок try/catch. Очень признателен, чувак!
Ответ №2:
Там нет обратного вызова с функцией отправки useState
.
const fn = async () =gt; { setIsLoading(true) const data = await fetchData() setData(data) setIsLoading(false) })
Вышеуказанная функция обычно вызывается при таком событии, как onClick
или useEffect
.
Комментарии:
1. Значит, функции набора состояния пользователя не выполняются асинхронно? Например, могу ли я быть уверен, что данные будут сохранены до того, как загрузка возобновится в false? (кстати, спасибо за ответ!)
Ответ №3:
в зависимости от того, как обычно реагирует на эти вызовы, вы получаете только один повторный рендеринг, но он не может сделать это в асинхронном обратном вызове (например, наши обработчики обещаний успеха и ошибок). Вы можете использовать объект для значения useState или использовать useReducer.
const [state,setState]=useState({isLoading:false,success:false,isError:false,data:[]})
затем установите эти свойства для каждого статуса.