Handling Asynchronous Data Fetching in React Beyond the Basics
react js react-queryHandling asynchronous data fetching in React
If you've ever fetched data in a React app, you've likely encountered a basic pattern like this:
This code fetches data from the PokemonAPI and displays it. While it's great for tutorials, you definitely wouldn’t want to use it in production unless you enjoy debugging at 3 AM. Let’s dive into some common pitfalls and how to avoid them.
1. Handling Loading and Error States
The initial example doesn’t handle loading and error states, leading to two major UX issues: cumulative layout shift and an infinite spinner. To address this, you should manage these states explicitly:
This approach ensures you can display appropriate feedback to the user while data is being fetched.
2. Avoiding Race Conditions
A tricky issue can occur when multiple asynchronous requests are made. For example,
if you click a button rapidly twice to change the Pokémon ID, a race condition might happen.
This can cause the UI to display incorrect data because your state with the id
will be out of sync
with the fetch call if the fetch for ID 1 comes second and the fetch for ID 2 comes first by happenstance.
Here how you can fix that:
I've written a full blog explaining race conditions, and I highly recommend checking it out: Race Condition.
3. Abstracting Data Fetching Logic
If you try to push that code to production, someone will definitely ask, 'Can you just hook it up as a custom hook?' 🪝 and you'll finish with code like this:
This hook encapsulates the data fetching logic, making it reusable across different components.
4. Managing Data Duplication and State
Okay, after addressing all the previous issues, you might realize that another component also needs access to the same endpoint data. It wouldn't make sense to send another request to the backend just to get the same results. Remember, one of your key responsibilities as a software developer is to reduce unnecessary costs and inefficiencies in your code.
When multiple components fetch the same data, it not only creates redundant requests but can also lead to inconsistencies. To prevent this, you should create a context to share the data across multiple components. Alternatively, you could lift the state up to a common parent component or use context to efficiently manage and distribute the data.
Are you still considering handling asynchronous data by yourself? I don't think so.
Before trying to solve a problem by myself, I always check if others have already solved it for us, and in most cases, they have. .
5. Leveraging React Query
To simplify data fetching and state management, consider using React Query. It efficiently handles caching, synchronization, and state updates. Look at how easy it is to fetch data without worrying about all the previous issues.
React Query abstracts away much of the complexity involved in handling asynchronous data and state, allowing you to focus on building features.
Conclusion
Asynchronous data fetching in React requires careful consideration beyond just making API calls. To build reliable and efficient applications, it’s crucial to:
- Handle loading and error states effectively to provide a smooth user experience.
- Avoid race conditions to ensure the UI always reflects the most current data.
- Abstract fetching logic into reusable hooks for cleaner and more maintainable code.
- Manage state duplication to prevent redundant requests and ensure data consistency across components.
- Leverage tools like React Query to simplify data fetching and state management.
By addressing these areas, you'll not only improve the robustness of your applications but also enhance the overall user experience, making your React apps more performant and user-friendly.