Счетчик загрузки не отображается в компоненте React - PullRequest
0 голосов
/ 21 июня 2020

Я создаю приложение React. js, которое имеет 2 компонента - основной из них является контейнером для 2-го и отвечает за получение информации из веб-API и затем передает ее дочернему компоненту, который отвечает за отображение информации в списке элементов. Предполагается, что отображающий компонент представляет счетчик загрузки при ожидании элементов данных от родительского компонента.

Проблема в том, что когда приложение загружается, я сначала получаю пустой список элементов, а затем все внезапно вся информация загружается в список без отображения счетчика. Сначала я получаю фильтр в одном из useEffects и на основе этой информации привожу сами элементы.

Родитель делает что-то вроде этого:

useEffect(() =>
{
    async function getNames()
    {
        setIsLoading(true);
        const names = await WebAPI.getNames();
        setAllNames(names);
        setSelectedName(names[0]);
        setIsLoading(false);
    };
    getNames();
  } ,[]);

  useEffect(() =>
  {
    async function getItems()
    { 
        setIsLoading(true);
        const items= await WebAPI.getItems(selectedName);
        setAllItems(items);
        setIsLoading(false);
    };
    getTenants();
  },[selectedName]);

  .
  .
  .
  return (
     <DisplayItems items={allItems} isLoading={isLoading} />
   );

И дочерние компоненты делают что-то вроде этого:

let spinner = props.isLoading ? <Spinner className="SpinnerStyle" /> : null; //please assume no issues in Spinner component
let items = props.items;
return (
   {spinner}
   {items}
)

Я предполагаю, что проблема в том, что setEffect является асинхронным, поэтому компонент сначала загружается с isLoading false, а затем все действие of setEffect вызывается перед фактическим изменением состояния? Поскольку здесь я предполагаю, что сначала я устанавливаю isLoading, затем выполняется повторная визуализация, а затем мы переходим к остальной части кода на useEffect. Не уверен как правильно сделать

Ответы [ 3 ]

1 голос
/ 21 июня 2020

вместо того, чтобы делать это так, как вы, я бы немного подправил его:

удалить setIsLoading(true); снизу

useEffect(() =>
{
    async function getNames()
    {
        setIsLoading(true); //REMOVE THIS LINE
        const names = await WebAPI.getNames();
        setAllNames(names);
        setSelectedName(names[0]);
        setIsLoading(false);
    };
    getNames();
  } ,[]);

и установить isLoading на true в исходном штат. Таким образом, он всегда будет показывать загрузку, пока вы явно не скажете ему этого не делать. т.е. когда у вас есть данные

, также измените рендеринг на это:

let items = props.items;
return isLoading ? (
   <Spinner className="SpinnerStyle" />
) : <div> {items} </div>
0 голосов
/ 23 июня 2020

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

0 голосов
/ 21 июня 2020

это полный пример с загрузкой:

const fakeApi = (name) =>
  new Promise((resolve)=> {
    setTimeout(() => {
      resolve([{ name: "Mike", id: 1 }, { name: "James", id: 2 }].filter(item=>item.name===name));
    }, 3000);
  })
  
  const getName =()=>   new Promise((resolve)=> {
    setTimeout(() => {
      resolve("Mike");
    }, 3000);
  })
const Parent = () => {
  const [name, setName] = React.useState();
   const [data, setData] = React.useState();
  const [loading, setLoading] = React.useState(false);
    const fetchData =(name) =>{
    if(!loading) setLoading(true);
      fakeApi(name).then(res=>
      setData(res)
      )
      }
    const fetchName = ()=>{
    setLoading(true);
    getName().then(res=> setName(res))
    }

  React.useEffect(() => {
    fetchName();
  }, []);
  React.useEffect(() => {
   if(name)fetchData(name);
  }, [name]);
    React.useEffect(() => {
  if(data && loading) setLoading(false)
  }, [data]);
  return (
    <div>
      {loading
        ? "Loading..."
        : data && data.map((d)=>(<Child key={d.id} {...d} />))}
    </div>
  );
};
const Child = ({ name,id }) =>(<div>{name}  {id}</div>)

ReactDOM.render(<Parent/>,document.getElementById("root"))
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

<div id="root"></div>
...