如何停止对 React 中的重新渲染进行 api 调用?

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

在我的主页上,我有这样的代码

{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 调用。 感谢您的帮助。

基本问题是,使用 select selected 选项卡的方式是安装和卸载组件。重新安装组件必然会重新运行任何安装 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 请求和状态提升到父组件并作为道具向下传递。或者使用 React 上下文做同样的事情并通过上下文提供状态。或者 implement/add 到全局状态管理,如 Redux/Thunks.

为了快速扩展 的答案,请考虑让您的选项卡成为选项卡组件的子项。这样,您的组件(稍微)更加解耦。

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>

我对这个问题的看法。您可以使用 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 的调用包装起来。