如何停止对 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
属性传递给 XList
和 YList
并根据 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 的调用包装起来。
在我的主页上,我有这样的代码
{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
属性传递给 XList
和 YList
并根据 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 的调用包装起来。