Code splitting in React causes API calls to be executed multiple times

Viewed 482

I am building a web application using React. I have used React Lazy with Webpack for code splitting but there is one issue that I have encountered. If I am on page A and I visit another screen (B) and now I re-visit the last screen (A) the screen is loaded again and the API calls in the useEffect hook of the component (A) is executed again.

My app does not have data that will constantly change so I dont want to call it again and again. What I concluded was that when the users visits another screen React unmounts the previous component and when the user visits again it mounts the component and executes the useEffect hook.

So, there are unnecessary API calls that I want to avoid. I know that I can cache my API calls but that feels like a workaround. Is there a better alternative to API caching or have I made some mistake in my code?

My app.js file where code splitting is implemented:

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

My StudentDashboard.container.js file:

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

You can create a wrapper component where you fetch the data once. Then store this data in ReactContext and use the context in your screen components.

Here is the solution:

  1. Create context for storing data
// 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. Create wrapper component to fetch and store data in the context
// 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. Use wrapper component in your app component or the component which wraps your navigation screens
// App.js
function App() {
  return (
    <DataProvider>
      <DataResolver>
        {/* Your views or routing logic */}
        <View>...</View>
      </DataResolver>
    </DataProvider>
  );
}
  1. Finally use the context in your screen component
// StudentDashboard.js
const StudentDashboard = ()=>{
  const [data] = useData(); // using custom hook you created in DataProvider.js
  ...
}
Related