#reactjs
#reactjs
Вопрос:
У меня есть компонент, который я использую для отображения списка записей данных, подобных этому (упрощенный):
// resource is the rest endpoint,
// items is the parents components
// state that holds the data entries and setItems is the corresponding setter
export default function LoadedList({resource,items, setItems,CustomFormat}){
const [loadingOrError,setLoadingOrError] =useState(false)
useEffect(()=>{
axios.get(baseURL resource)
.then((e)=>{
setItems(e.data)
setLoadingOrError(false)
})
.catch((e)=>{
setItems([{text:"Error"}])
setLoadingOrError(true)
})
setItems([{text:"Loading...-"}])
setLoadingOrError(true)
},[])
return(
<div className="list">
{
items.map((item)=>
loadingOrError?
<DefaultFormat item={item} />
:
<CustomFormat item={item}/>
)
}
</div>
)
}
Основная идея заключается в том, что пока компонент загружает элемент или в случае сбоя, для отображения соответствующего сообщения следует использовать формат по умолчанию.
После успешной загрузки элементов для форматирования записей следует использовать формат родительского элемента.
Проблема в том, что я обнаружил, что setItems и setLoading не изменяются одновременно. Похоже, что это работает так, что сначала он устанавливает элементы, затем повторно отправляет все записи и только затем изменяет loadingOrError на true. Итак, есть ли способ установить оба из них одновременно? Или просто без повторного воспроизведения всего, что находится между ними?
Ответ №1:
Вместо того, чтобы пытаться обновлять оба одновременно, почему бы вам не попробовать отслеживать состояние загрузки и ошибки отдельно, а затем сделать что-то вроде этого:
// resource is the rest endpoint,
// items is the parents components
// state that holds the data entries and setItems is the corresponding setter
export default function LoadedList({resource, items, setItems, CustomFormat}){
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
useEffect(()=>{
setLoading(true);
axios.get(baseURL resource)
.then((e)=>
setItems(e.data)
)
.catch((e)=>
setError("Error")
)
.finally(() => setLoading(false));
},[])
if(loading) {
return "Loading ...";
}
if(error) {
return error;
}
return(
<div className="list">
{items.map((item, index) => <CustomFormat key={index} item={item}/>)}
</div>
)
}
Это должно отображаться Loading...
до тех пор, пока не будут загружены все элементы.
Если вы настаиваете на том, чтобы оставить все как есть, и просто добиться того, что вы изначально просили об обновлении обоих одновременно, вам, вероятно, потребуется определить функцию, которая выполняет вызов API на один уровень выше, вместе с состоянием загрузки, состоянием ошибки и обработкой состояния данных, имеют все эти укажите вместе под одним и тем же хуком состояния, а затем передайте вниз функцию API, которая будет использоваться в дочернем useEffect.
const [dataState, setDataState] = useState({
data: null,
loading: false,
error: ""
});
...
setDataState({data: data, loading: false});
Помимо этого, я рекомендую две вещи:
- Вы должны убедиться, что компонент все еще смонтирован после завершения запроса и непосредственно перед установкой состояния. В противном случае вы получите сообщение об ошибке. Этого очень просто добиться с помощью дополнительной переменной для отслеживания состояния монтирования.
- Может быть полезно создать пользовательский хук для обработки запросов, поскольку это, вероятно, то, что вы будете делать часто, и в каждом случае это будет выглядеть очень похоже. Я нахожу пошаговое руководство в этом посте очень понятным.
Взято из этого сообщения:
Пользовательский хук useFetch
import { useState, useEffect } from 'react';
const useFetch = (url = '', options = null) => {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
let isMounted = true;
setLoading(true);
fetch(url, options)
.then(res => res.json())
.then(data => {
if (isMounted) {
setData(data);
setError(null);
}
})
.catch(error => {
if (isMounted) {
setError(error);
setData(null);
}
})
.finally(() => isMounted amp;amp; setLoading(false));
return () => (isMounted = false);
}, [url, options]);
return { loading, error, data };
};
export default useFetch;
Комментарии:
1. спасибо, это отвечает на мой вопрос. Я все еще считаю странным, что react настаивает на промежуточном рендеринге. Я также не знал, что мне нужно отслеживать состояние монтирования