Improving performance in mobile apps is always a balancing act — especially when it comes to data fetching. You want to keep things fast, avoid unnecessary API calls, and ensure your users always see the most up-to-date information.
Recently, I ran into this exact challenge while working on a React Native app. I had a screen that fetched a list of specific entity based on a user ID. The data didn't change often, and yet every time the screen loaded, we made a fresh network call.
So, can we cache this response natively and skip the request unless it's really needed? The answer: yes — and React Native already gives us what we need.
Let’s walk through what I did.
The Problem
Imagine this:
fetch(`http://api.example.com/entity/123`)
You make this call when a user opens the app or navigates to a screen. Even if the data hasn't changed, the app hits the backend every time. This not only slows down the UI but also increases server load.
React Native, under the hood, uses:
- OkHttp on Android
- NSURLSession on iOS
Both of these support HTTP caching — meaning you can cache GET responses natively based on headers like Cache-Control.
But by default, React Native doesn't configure OkHttp for disk caching. So we first needed to get our backend and frontend talking the same cache language.
Step 1: Let the Backend Control the Cache
We updated our API to include a proper Cache-Control
header:
Cache-Control: public, max-age=3600, must-revalidate
What does that mean?
public
: this response can be cached by clients and shared caches (like proxies or CDNs)max-age=3600
: cache is fresh for 1 hourmust-revalidate
: after 1 hour, the client must check with the server before reusing it
That tells native clients like OkHttp and NSURLSession: “You can safely cache this response for an hour.”
Step 2: Overriding the Cache When Needed
Now here’s the twist.
Sometimes, we wanted to force a fresh request, even if the cache was still valid — like when:
- The app is reopened
- The user manually refreshes the screen
- We suspect stale data for any reason
We solved this by adding a Cache-Control: no-cache
header on demand:
fetch('http://api.example.com/entity/123', {
method: 'GET',
headers: {
'Cache-Control': 'no-cache',
},
})
.then(response => response.json())
.then(data => {
console.log('Fresh response:', data)
})
.catch(error => {
console.error('Fetch error:', error)
})
Final Thoughts
Caching can feel like a black box — especially in mobile apps — but understanding how native caching works under the hood unlocks a lot of power.
By:
- Letting the backend control cache timing
- Letting React Native honor the native cache
- And providing a clean way to override it when needed
We got the best of both worlds: performance + flexibility.
This flow took a bit to learn, but once everything clicked, it became one of those “ohhh, this is super useful” moments.
Let me know if you want a full code example or a working demo!
Top comments (0)