-1

There is a code, in it I create links by data and put into an array:

function Blogs(props) {
    const links = [];
    props.ids.forEach(id => {
        fetch(`${root}/api/blog/${id}`)
            .then(res => res.json())
            .then(res => {
                const link = <Link to={`/blog/${id}`}>{_.get(res, 'blog.name', '')}</Link>
                links.push(link);
            })
    });
    console.log(links.length) // 0
    return (
        <div className="profile-blogs">
            <div className="a">Блоги</div>
            <div className="b">
                {links}               {/* nothing */}
            </div>
        </div>
    );
}

When I consoling a links:

enter image description here

Why links.length === 0 and how to fix it?

kertAW
  • 264
  • 3
  • 12
  • 4
    `fetch()` is **asynchronous**. The `.then()` functions will run when the HTTP request completes. – Pointy Apr 19 '21 at 18:45
  • Your `console.log` is written **after** the `fetch` but in reality `fetch` is **called after** because it needs time to fetch... so the code moves on to the `console.log` while the `fetch` code is still waiting for data. This is a *simplified* explanation. – vsync Apr 19 '21 at 18:47
  • You need to watch [this](https://www.youtube.com/watch?v=8aGhZQkoFbQ). – Medi Apr 19 '21 at 18:54
  • Does this answer your question? [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Emile Bergeron Apr 19 '21 at 18:58

2 Answers2

1

TL;DR do below changes :

async function Blogs(props) {
  const links = [];
  for(let id of props.ids){
       const res = await fetchLink(id);
       const link = <Link to={`/blog/${id}`}>{_.get(res, 'blog.name', '')}</Link>
       links.push(link);
  }
  const fetchLink = async(id) =>{
    const result = await  fetch(`${root}/api/blog/${id}`);
    return result.json();
  }
  console.log(links.length) // 0
  return (
      <div className="profile-blogs">
          <div className="a">Блоги</div>
          <div className="b">
              {links}               {/* nothing */}
          </div>
      </div>
  );
}

But Why my code is not working ?

JS is asynchronous , it doesn't wait for any Async statement to finish. So if not handled properly, the next line of code which is intended to be executed after the async operation will execute. forEach method will not execute your async code serially (I guess that is your assumption). So in the loop, all the element will run one by one (calling the fetch API) without waiting for the previous one to get completed(that is how JS works). Finally it reaches to the next line i.e console.log(links.length) which prints 0 because your API call never finished before executing this line.

Another important point :

Executing this kind of code in the component is not advisable because it will be re-executed every time the state is updated and component is re-rendered. Such kind of code should be executed as sideEffect probably using the useEffect hook. So that the code block is only executed when needed and not every time the component is rendered.

programoholic
  • 4,830
  • 5
  • 20
  • 59
-1

You need to use two of the react hooks; useState for links array, useEffect for fetch function.

İlker
  • 1,872
  • 1
  • 12
  • 28