【问题标题】:Code splitting in React causes API calls to be executed multiple timesReact 中的代码拆分导致 API 调用被执行多次
【发布时间】:2020-07-18 14:03:36
【问题描述】:

我正在使用 React 构建一个 Web 应用程序。我已经将 React Lazy 与 Webpack 一起用于代码拆分,但我遇到了一个问题。如果我在页面 A 并访问另一个屏幕 (B),现在我重新访问最后一个屏幕 (A),屏幕将再次加载,并且组件 (A) 的 useEffect 挂钩中的 API 调用将再次执行。

我的应用没有会不断变化的数据,所以我不想一次又一次地调用它。我得出的结论是,当用户访问另一个屏幕时,React 会卸载前一个组件,而当用户再次访问时,它会安装该组件并执行 useEffect 钩子。

因此,我想避免不必要的 API 调用。我知道我可以缓存我的 API 调用,但这感觉像是一种解决方法。是否有更好的 API 缓存替代方案,或者我在代码中犯了一些错误?

我实现代码拆分的app.js文件:

const LoginContainer = lazy(()=> import('./Container/StudentDashboard.container'));

我的 StudentDashboard.container.js 文件:

const StudentDashboard = ()=>{
  useEffect(()=>{
    (async()=>{
      const data = await fetch({...}) # this API call is executed every time user visits the screen
     })
  },[])
}

【问题讨论】:

  • 尝试发送一个空的依赖数组到useEffect
  • @ludwiguer 它有一个空的依赖数组
  • 是什么让您认为这与代码拆分有关?如果不使用惰性组件,它的行为是否相同?如果您的问题是组件正在卸载和重新安装,那么您将需要找到一种方法将状态保留在组件之外,以便在组件卸载时不会丢失。这里没有足够的信息来了解这两个组件是如何组成的,为什么StudentDashboard 可能会被卸载,以及该状态应该保存在哪里。
  • 在代码被多次执行的情况下,我总是使用console.log 来了解双重订阅发生的位置。尝试将console.log 放在您发送 api-call 的地方,一个放在订阅方法的地方,依此类推...
  • @rfestag 没有惰性组件,屏幕永远不会卸载,api 调用不会多次执行

标签: javascript reactjs react-native react-hooks


【解决方案1】:

您可以创建一个包装器组件,在其中获取一次数据。然后将此数据存储在ReactContext 中并在屏幕组件中使用上下文。

解决办法如下:

  1. 为存储数据创建上下文
// DataProvider.js
const DataContext = React.createContext(null);

const useData = () => React.useContext(DataContext); // create custom hook to use data on different screens

const DataProvider = ({ children }) => (
  <DataContext.Provider value={React.useState(null)}>
    {children}
  </DataContext.Provider>
);
  1. 创建包装器组件以在上下文中获取和存储数据
// DataResolver.js
function DataResolver({children}) {
  const [storedData, setStoredData] = useData(); // custom hook created in DataProvider.js

  useEffect(() => {
    const fetchAndStoreData = async () => {
      const data = await fetch({...});
      setStoredData(data);
    };

    if (!storedData) { // OR any your custom condition
      fetchAndStoreData();
    }
  },[]);

  return children;
}
  1. 在您的应用组件或包装您的导航屏幕的组件中使用包装器组件
// App.js
function App() {
  return (
    <DataProvider>
      <DataResolver>
        {/* Your views or routing logic */}
        <View>...</View>
      </DataResolver>
    </DataProvider>
  );
}
  1. 最后在屏幕组件中使用上下文
// StudentDashboard.js
const StudentDashboard = ()=>{
  const [data] = useData(); // using custom hook you created in DataProvider.js
  ...
}

【讨论】:

  • 我真的很喜欢这种方法,但我认为我可以在上下文本身中创建数据解析器函数并将数据本身存储在那里
  • @RamPandey 不确定我是否理解你,建议的方法实际上是在上下文中存储数据。它只需要在某个地方获取,以便 DataResolver 组件用于此目的。请注意,您无法在 App 组件中获取数据并将数据传递给上下文,因为 App 未使用 DataProvider 包装
猜你喜欢
  • 2021-04-19
  • 1970-01-01
  • 2015-09-26
  • 1970-01-01
  • 2018-07-26
  • 2019-06-12
  • 2021-12-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多