简体   繁体   中英

Can't map fetched data on first render in React

I have an API that returns me 4 objects and I try to save them in a new array called "products" and map them to the DOM. My problem is that map function doesn't work on first render.

If I modify something in my code, save the file and watch the react page again, I will see that my data appeared.

How can I fix that?

import { useEffect, useState } from 'react';
import './App.css';

function App() {

  const [products, setProducts] = useState([]);

  useEffect(() => {
    fetch("URL")
    .then(res => res.json())
    .then(data => {
      for(let i = 0; i < data.length; i++) {
        products.push(data[i])
      }
    })
  })

  console.log(products);

  return (
    <div className="App">
      {products.map((product,index) => (
        <div key={index}>{product.price}</div>
      ))}
    </div>
  );
}

export default App;

const [products, setProducts] = useState([]);

useEffect(() => {
    async function getdata() {      
      const res = await fetch("URL");
      const data = await res.json();
      setProducts(data)
    }

    let unmounted = false;    
    if (!unmounted) {
      getdata();
    }

    return () => unmounted = true;
}, []);

You can try this, I hope It will be work.

You're directly mutating state which is a React no-no.

You need to use the setProducts state setter instead

useEffect(() => {
  fetch("URL")
    .then(async res => {
      if (!res.ok) { // don't forget to check for errors
        throw new Error(`${res.status}: ${await res.text()}`);
      }
      return res.json();
    })
    .then(setProducts) // pass the resolved data to the state setter
    .catch(console.error);
}, []); // use an empty dependency array to only run once on mount

Here's a demo using the JSONPlaceholder /albums API

编辑 romantic-galois-0jvkzt

First of all, you are missing dependency array in your useEffect hook. Assuming you want the useEffect hook to run on initial render, I am passing empty dependency array ( [] ).

Next fix is you wait for your data to be set in products, and if products are set (that is you have a products.length > 0 ), you call

useEffect(() => {
    fetch("URL")
    .then(res => res.json())
    .then(data => setProducts(data))
  }, [])

  console.log(products);

  return (
    <div className="App">
      {products.length && products.map((product,index) => (
        <div key={index}>{product.price}</div>
      ))}
    </div>
  );

you have to run useEffect() only once then u need to call setProducts

useEffect(() => {
    fetch("URL")
      .then(res => res.json())
      .then(data => {
        setProducts(data);
      })
  },[])

The data is fetched after the first render. You might want to use

{Boolean(products.length) && products.map((product,index) => (
    <div key={index}>{product.price}</div>
  ))}

and

useEffect(() => {
fetch("URL")
.then(res => res.json())
.then(data => {
  for(let i = 0; i < data.length; i++) {
    products.push(data[i])
  }
  setProducts(products);
})
})

instead

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