简体   繁体   中英

Loading spinner not showing in React Component

I am creating a React.js app which got 2 components - The main one is a container for the 2nd and is responsible for retrieving the information from a web api and then pass it to the child component which is responsible for displaying the info in a list of items. The displaying component is supposed to present a loading spinner while waiting for the data items from the parent component.

The problem is that when the app is loaded, I first get an empty list of items and then all of a sudden all the info is loaded to the list, without the spinner ever showing. I get a filter first in one of the useEffects and based on that info, I am bringing the items themselves.

The parent is doing something like this:

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} />
   );

And the child components is doing something like this:

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

I'm guessing that the problem is that the setEffect is asynchronous which is why the component is first loaded with isLoading false and then the entire action of setEffect is invoked before actually changing the state? Since I do assume here that I first set the isLoading and then there's a rerender and then we continue to the rest of the code on useEffect. I'm not sure how to do it correctly

The problem was with the asynchronicity when using mulitple useEffect. What I did to solve the issue was adding another spinner for the filters values I mentioned, and then the useEffect responsible for retrieving the values set is loading for that specific spinner, while the other for retrieving the items themselves set the isLoading for the main spinner of the items.

instead of doing it like you are I would slightly tweak it:

remove setIsLoading(true); from below

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

and have isLoading set to true in your initial state. that way, it's always going to show loading until you explicitly tell it not to. ie when you have got your data

also change the rendering to this:

let items = props.items;
return isLoading ? (
   <Spinner className="SpinnerStyle" />
) : <div> {items} </div>

this is full example with loading:

 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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM