【问题标题】:How to stop making api call on re-rendering in React?如何停止在 React 中重新渲染时进行 api 调用?
【发布时间】:2022-01-24 05:17:17
【问题描述】:

在我的主页中,我有类似这样的代码

{selectedTab===0 && <XList allItemList={some_list/>}
{selectedTab===1 && <YList allItemList={some_list2/>}

现在,在 XList 中,我有这样的东西:

{props.allItemList.map(item => <XItem item={item}/>)}

现在,在 XItem 内部,我正在调用一个 api 来获取 XItem 的图像。

现在我的问题是当在主页时,我将选项卡从 0 切换到 1 或从 1 切换到 0,它再次调用 XItem 中的所有 API。每当我切换标签时,它都会再次调用 api。我不想那样。我已经在 XItem 中使用了 useEffect,其中 [] 数组作为第二个参数。

我有我的后端代码,我在其中制作了一个 api 来获取 XItem 的图像。 API直接返回图片而不是url,所以我不能一次调用所有api。

我需要一些解决方案,以便最大限度地减少 api 调用。 感谢您的帮助。

【问题讨论】:

  • 你需要了解useMemouseCallback react hook,google一下就行了。
  • 看看 react-query,它优雅地解决了这些问题。
  • 状态管理

标签: javascript reactjs api react-hooks use-effect


【解决方案1】:

基本问题是,通过您选择所选选项卡的方式,您可以安装和卸载组件。重新挂载组件必然会重新运行任何挂载useEffect 回调,这些回调会发出网络请求并将任何结果存储在本地组件状态中。卸载组件必然会处理组件状态。

{selectedTab === 0 && <XList allItemList={some_list} />}
{selectedTab === 1 && <YList allItemList={some_list2} />}

一种解决方案是将isActive 属性传递给XListYList,并根据selectedTab 值设置值。每个组件根据 isActive 属性有条件地呈现其内容。想法是保持组件安装,以便它们在最初安装时只获取一次数据。

<XList allItemList={some_list} isActive={selectedTab === 0} />
<YList allItemList={some_list2} isActive={selectedTab === 1} />

例如XList

const XList = ({ allItemList, isActive }) => {
  useEffect(() => {
    // expensive network call
  }, []);

  return isActive 
    ? props.allItemList.map(item => <XItem item={item}/>)
    : null;
};

替代方法包括将 API 请求和状态提升到父组件并作为 props 向下传递。或者使用 React 上下文来做同样的事情并通过上下文提供状态。或者实现/添加到像 Redux/Thunks 这样的全局状态管理。

【讨论】:

  • 嗨@Drew,感谢您的回答。您的第一个解决方案效果很好。只是想知道如何使用 React Context 来实现这一点。如果我创建一个上下文,那么这意味着我需要一次调用所有 XItem 的所有 api。正如我所说,我正在调用 api 来获取图像,并且服务器正在以二进制文件而不是 url 形式发送图像,因此可能需要很长时间。目前,当我加载时,所有 XItem 都已加载,并定期显示图像。但是使用上下文后,它将等待所有图像加载一次。这是我的理解。如果我错了,请纠正我。
【解决方案2】:

只是为了快速扩展Drew Reese 的答案,请考虑让您的选项卡成为选项卡组件的子级。这样一来,您的组件就(稍微)解耦了。

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>
  );
};

以及处理 isActive 属性的包装器

const Tab = ({ isActive, children }) => <div hidden={!isActive}>{children}</div>

然后像这样渲染它们


<MyTabulator>
   <Tab name="x list"><XList allItemList={some_list} /></Tab>
   <Tab name="y list"><YList allItemList={some_list2} /></Tab>
</MyTabulator>

【讨论】:

    【解决方案3】:

    我对这个问题的看法。您可以使用React.memo 包装XItem 组件:

    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);
    

    如果你选择使用useMemo,逻辑是一样的。

    如果你想使用useCallback(我的默认选择)只需要包装你对 api 的调用。

    【讨论】:

      猜你喜欢
      • 2019-12-14
      • 2018-02-22
      • 2018-07-20
      • 1970-01-01
      • 2020-12-03
      • 1970-01-01
      • 2021-07-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多