最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to stop making api call on re-rendering in React? - Stack Overflow

programmeradmin7浏览0评论

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 and useCallback 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
Add a ment  | 

3 Answers 3

Reset to default 4

The 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.

发布评论

评论列表(0)

  1. 暂无评论