In my homepage, I have code something like this
{selectedTab===0 && <XList allItemList={some_list/>}
{selectedTab===1 && <YList allItemList={some_list2/>}
Now, In XList, I have something like this:
{props.allItemList.map(item => <XItem item={item}/>)}
Now, Inside XItem, I am calling an api to get the image of XItem.
Now my problem is When In homepage, I switched the tab from 0 to 1 or 1 to 0, It is calling all the API's in XItem again. Whenever I switched tab it calls api again. I don't want that. I already used useEffect inside XItem with [] array as second parameter.
I have my backend code where I made an api to get the image of XItem. The API is returning the image directly and not the url, so I can't call all api once.
I need some solution so that I can minimize api call. Thanks for help.
In my homepage, I have code something like this
{selectedTab===0 && <XList allItemList={some_list/>}
{selectedTab===1 && <YList allItemList={some_list2/>}
Now, In XList, I have something like this:
{props.allItemList.map(item => <XItem item={item}/>)}
Now, Inside XItem, I am calling an api to get the image of XItem.
Now my problem is When In homepage, I switched the tab from 0 to 1 or 1 to 0, It is calling all the API's in XItem again. Whenever I switched tab it calls api again. I don't want that. I already used useEffect inside XItem with [] array as second parameter.
I have my backend code where I made an api to get the image of XItem. The API is returning the image directly and not the url, so I can't call all api once.
I need some solution so that I can minimize api call. Thanks for help.
Share Improve this question edited Dec 23, 2021 at 6:35 Drew Reese 204k18 gold badges245 silver badges273 bronze badges asked Dec 23, 2021 at 6:06 UnknownUnknown 4371 gold badge5 silver badges12 bronze badges 3-
2
you need to learn about
useMemo
anduseCallback
react hook, just google it. – Pradip Dhakal Commented Dec 23, 2021 at 6:27 - 1 Take a look at react-query, it solves problems like these elegantly. – Ian Mercer Commented Dec 23, 2021 at 6:37
- State Management – Pieterjan Commented Dec 23, 2021 at 6:54
3 Answers
Reset to default 4The basic issue is that with the way you select the selected tab you are mounting and unmounting the ponents. Remounting the ponents necessarily re-runs any mounting useEffect
callbacks that make network requests and stores any results in local ponent state. Unmounting the ponent necessarily disposes the ponent state.
{selectedTab === 0 && <XList allItemList={some_list} />}
{selectedTab === 1 && <YList allItemList={some_list2} />}
One solution could be to pass an isActive
prop to both XList
and YList
and set the value based on the selectedTab
value. Each ponent conditionally renders its content based on the isActive
prop. The idea being to keep the ponents mounted so they only fetch the data once when they initially mounted.
<XList allItemList={some_list} isActive={selectedTab === 0} />
<YList allItemList={some_list2} isActive={selectedTab === 1} />
Example XList
const XList = ({ allItemList, isActive }) => {
useEffect(() => {
// expensive network call
}, []);
return isActive
? props.allItemList.map(item => <XItem item={item}/>)
: null;
};
Alternative means include lifting the API requests and state to the parent ponent and passing down as props. Or using a React context to do the same and provide out the state via the context. Or implement/add to a global state management like Redux/Thunks.
Just to quickly expand on Drew Reese's answer, consider having your tabs be children of a Tabs-ponent. That way, your ponents are (slightly) more decoupled.
const MyTabulator = ({ children }) => {
const kids = React.useMemo(() => React.Children.toArray(children), [children]);
const [state, setState] = React.useState(0);
return (
<div>
{kids.map((k, i) => (
<button key={k.props.name} onClick={() => setState(i)}>
{k.props.name}
</button>
))}
{kids.map((k, i) =>
React.cloneElement(k, {
key: k.props.name,
isActive: i === state
})
)}
</div>
);
};
and a wrapper to handle the isActive prop
const Tab = ({ isActive, children }) => <div hidden={!isActive}>{children}</div>
Then render them like this
<MyTabulator>
<Tab name="x list"><XList allItemList={some_list} /></Tab>
<Tab name="y list"><YList allItemList={some_list2} /></Tab>
</MyTabulator>
My take on this issue. You can wrap XItem ponent with React.memo:
const XItem = (props) => {
...
}
const areEqual = (prevProps, nextProps) => {
/*
Add your logic here to check if you want to rerender XItem
return true if you don't want rerender
return false if you want a rerender
*/
}
export default React.memo(XItem, areEqual);
The logic is the same if you choose to use useMemo
.
If you want to use useCallback
(my default choice) would require only to wrap the call you make to the api.