I'm trying to show list of items that i get from an API in the backend. However, when I try to map the array, then it doesn't show any item. What am I doing wrong? Anyone could help please?
I tried to console log and I can see list of items being fetched correctly. The h2 tag also displays. but not the .map results.
This is the code:
interface Props {
products: Product[] | [];
}
const ProductsList = ({ products }: Props) => {
useEffect(() => {
getProducts();
console.log('fetched data', getProducts());
}, []);
return (
<RootContainer>
<h2>test</h2>
{products &&
products.map((product) => {
return <ProductItem key={product.id} product={product} />;
})}
</RootContainer>
);
};
export default ProductsList;
This is the getProducts() function that fetch data from another port.
const getProducts = async () => {
let API_URL = 'http://localhost:5000/api/Products';
const response: any = await fetch(API_URL);
const data = await response.json();
return data;
};
export default getProducts;
CodePudding user response:
I think the issue may be that you are retrieving the products but not actually doing anything with them - therefore the "products" check you perform before iterating will return false.
Try using a state variable:
const ProductsList = () => {
const [products, setProducts] = useState<product[]>();
useEffect(() => {
getProducts().then((result) => setProducts(result);
}, []);
return (
<RootContainer>
<h2>test</h2>
{products &&
products.map((product) => {
return <ProductItem key={product.id} product={product} />;
})}
</RootContainer>
);
};
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
The principal here is that when the getProducts() method returns it will update the state variable with a list of products. This change in state will cause the component to refresh and the products.map will be reached.
CodePudding user response:
You are fetching the products on the useEffect
hook but are not storing them as a state value. Try using useState
to achieve that, and try tweaking your rendering logic.
import {useState, useEffect} from 'react';
//...
interface Props {
products: Product[] | [];
}
const ProductsList = (props: Props) => {
const [products, setProducts] = useState<Product[]>([]);
useEffect(() => {
const _products = getProducts();
console.log('fetched data',_products);
setProducts(_products);
}, []);
return (
<RootContainer>
<h2>test</h2>
{products &&
products.map((product) => {
return <ProductItem key={product.id} product={product} />;
})}
</RootContainer>
);
};
export default ProductsList;
If you still don't see anything, try modifying your rendering logic like so:
<RootContainer>
<h2>test</h2>
{products && products.map((product) =>
(<ProductItem key={product.id} product={product} />)
)}
</RootContainer>
Since you are using TypeScript, conditional rendering needs to be handled a bit differently than from JS. So in your check for products &&
try this:
<RootContainer>
<h2>test</h2>
{products ? products.map((product) => (
<ProductItem key={product.id} product={product} />)
: null
)}
</RootContainer>
Now, depending on the props you've defined for the RootContainer component, passing a null as a child
might not be allowed. If that's the case, try changing the null
to <></>
to pass a React Fragment instead