Reputation: 27
I have looked at every infinite loop answer in SO and I cannot figure out what I'm doing wrong.
I'm trying to fetch some data and set my productList state with the data but it causes an infinite loop.
export default (props) => {
const [productsList, setProductsList] = useState([]);
const getProducts = async () => {
try {
await axios
.get("https://api.stripe.com/v1/skus", {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${StripeKey}`,
},
})
.then(({ data }) => {
setProductsList(data.data);
});
} catch {
(error) => {
console.log(error);
};
}
};
useEffect(() => {
getProducts();
}, [productList]);
return (
<ProductsContext.Provider value={{ products: productsList }}>
{props.children}
</ProductsContext.Provider>
);
};
I have also tried with having an empty array at the end of useEffect and that causes it to not set the state at all.
What am I missing?
EDIT:
I removed the try/catch and [productList] from useEffect
import React, { useState, useEffect } from "react";
import axios from "axios";
const StripeKey = "TEST_KEY";
export const ProductsContext = React.createContext({ products: [] });
export default (props) => {
const [productsList, setProductsList] = useState([]);
useEffect(() => {
axios({
method: "get",
url: "https://api.stripe.com/v1/skus",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${StripeKey}`,
},
})
.then((response) => {
setProductsList(response.data.data)
})
.catch((error) => {
console.log(error);
});
}, []);
return (
<ProductsContext.Provider value={{ products: productsList }}>
{props.children}
</ProductsContext.Provider>
);
};
Upvotes: 0
Views: 562
Reputation: 1033
About how you fetch your data: I think the problem might be on how you are fetching data:
As stated in docs I think you should not be using try catch here, but something like:
function MyComponent() {
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [items, setItems] = useState([]);
// Note: the empty deps array [] means
// this useEffect will run once
// similar to componentDidMount()
useEffect(() => {
fetch("https://api.example.com/items")
.then(res => res.json())
.then(
(result) => {
setIsLoaded(true);
setItems(result.items);
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
setIsLoaded(true);
setError(error);
}
)
}, [])
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<ul>
{items.map(item => (
<li key={item.name}>
{item.name} {item.price}
</li>
))}
</ul>
);
}
}
About the useEffect callback:
I don't think you should use [productList] as its the item you are updating, so it would trigger again once you do the setProducts. You might want to do the fetching again when a prop of the request changes or just on component did mount (empty array as you said)
Also, there might be other side effects we cannot see with that sample of code. Maybe you could share an stackblitz to be sure
Upvotes: 2