I'm looking for the most optimal way to fetch data using useEffect()
when the fetch function is used in more than one place.
Currently, I have a parent component (ItemContainer
) and a child component (SearchBar
). ItemContainer
should fetch the all the possible list of items using getItemList()
functions. I'm executing this function within the useEffect()
during the first render, and also passing it down to SearchBar
component, so that when a user submits a search term, it will update itemList
state by triggering getItemList()
in ItemContainer
.
This actually works just as I expected. However, my issue is that
getItemList()
outside the useEffect()
in this kind of situation. From what I've been reading (blog posts, react official docs) it is generally recommended that data fetching function should be defined inside the useEffect()
, although there could be some edge cases. I'm wondering if my case applies as this edge cases.useCallback
? I tried filling it out using searchTerm
, itemList
, but none of them worked - and I'm quite confused why this is so.I feel bad that I don't fully understand the code that I wrote. I would appreciate if any of you could enlighten me with what I'm missing here...
const ItemContainer = () => {
const [itemList, setItemList] = useState([]);
const getItemList = useCallback( async (searchTerm) => {
const itemListRes = await Api.getItems(searchTerm);
setItemList(itemListRes)
}, []);
useEffect(() => {
getItemList();
}, [getItemList]);
return (
<main>
<SearchBar search={getItemList} />
<ItemList itemList={itemList} />
</main>
)
}
const SearchBar = ({ search }) => {
const [searchTerm, setSearchTerm] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
search(searchTerm);
setSearchTerm('');
}
const handleChange = (e) => {
setSearchTerm(e.target.value)
}
return (
<form onSubmit={handleSubmit}>
<input
placeholder='Enter search term...'
value={searchTerm}
onChange={handleChange}
/>
<button>Search</button>
</form>
)
}
Here are my answers.
Yes, it is okay. What's inside useCallback
is "frozen" respect to
the many ItemConteiner
function calls that may happen. Since the
useCallback
content accesses only setItemList
, which is also a
frozen handler, there'll be no problems.
That's also correct, because an empty array means "dependent to
nothing". In other words, the callback is created once and keeps
frozen for all the life of the ItemContainer
.
Instead, this is something weird:
useEffect(() => {
getItemList();
}, [getItemList]);
It works, but it has a very little sense. The getItemList
is created once only, so why make an useEffect
depending to something never changes?
Make it simpler, by running once only:
useEffect(() => {
getItemList();
}, []);