Reputation: 2609
Scenario -
Suppose, there is an API which gives a list of books.
[ { id:1,
name: "A",
author: "https://author/1"
},
{ id:2,
name: "B",
author: "https://author/10"
},
{ id:3,
name: "A",
author: "https://author/3"
},
{ id:4,
name: "A",
author: "https://author/1"
}
...
]
Now, I need to call the author API to get the author details, but I need to avoid calling those APIs which has already been called.
I tried doing it by keeping a state object and updating the object whenever a new api is called. If the API URL already has an entry in the object, that call is not repeated. However, I suspect because of async nature of setState, the update to the state Object are clubbed and in the subsequent iteration, when I check the object to find the previous entry, it doesn't reflect.
...
const[authorDetail, setAuthorDetail] = useState({});
useEffect(() => {
for(let i=0;i<books.length;i++)
{
if(!authorDetail[books[i].author]) {
// make API call to fetch author detail
authorDetail[books[i].author] = "called"
setState({...authorDetail});
}
});
How can I optimise the API call for this case. For example, if I have 7 Harry Potter books, I would like to make only one call to fetch J.K. Rowling's data.
Upvotes: 0
Views: 87
Reputation: 238
I believe the problem here is that your effect is maintaining a reference to the old state object. I think the easiest way to solve it will be by using a ref object.
const authorDetail = useRef({});
useEffect(() => {
for (let i = 0;i < books.length; i++) {
if (!authorDetail.current[books[i].author]) {
// make API call to fetch author detail
authorDetail.current[books[i].author] = "called"
}
}
}, [books]); // note the dependencies here!
books
is a dependency for the effect, but you could also provide empty square brackets ([]
) which will force it to only run when the component mounts.
Upvotes: 1