Video and Lottie Caching in React Native
In our recent efforts to enhance the performance and reduce the size of our React Native app, we have implemented two types of caching: Video Caching and Lottie Caching. Both caching mechanisms utilize disk space to store the cached content, allowing for efficient retrieval and display.
The Problem with Downloading Content
When it comes to downloading content, we often face a dilemma: should we download all items at once, or should we download them on demand? The choice of approach depends on the nature of the content and its usage patterns.
Proposed Solutions
For Lottie animations, we chose to download all Lottie URLs at once. Since the Lottie URLs do not change frequently, this approach ensures that the app always has the latest Lottie animations readily available. This reduces the number of network requests, resulting in improved performance and a more seamless user experience.
In contrast, for video caching, we adopted a different approach. Since video URLs change frequently based on backend logic, we download videos on demand. This approach ensures that the app only downloads the videos that are currently needed, reducing the overall size of the app and minimizing the risk of outdated content.
The Wrong Approach
One common mistake is to handle caching logic directly within components without using a wrapper class. This can lead to code duplication and makes it difficult to manage caching logic across different parts of the app.
The Best Solution: Using a Wrapper Class
To solve this, we use a wrapper class for read and write related logic. This not only simplifies the code but also makes it easier to manage changes. Here is an example of a wrapper class for Lottie caching:
*const LottieWrapper = props => {
const lottieUrl = props?.lottieUrl ? props?.lottieUrl : '';
const [url, setUrl] = useState(null);
const dispatch = useDispatch();
const lottieName = lottieUrl?.substring(lottieUrl?.lastIndexOf('/') + 1);
const lottieDetails = useSelector(state => state.appReducer.lottieDetails);
useEffect(() => {
const path = getCachePath();
fs.exists(path)
.then(exists => {
if (exists) {
readDownloadedLottie(path, res => {
setUrl(res);
});
} else {
if (lottieUrl && lottieUrl?.includes('.json')) {
getLottieCacheUrl(
lottieUrl,
lottieName,
(downloadPath, fileSize) => {
readDownloadedLottie(downloadPath, res => {
setUrl(res);
});
}
);
} else {
setUrl(lottieUrl);
}
}
})
.catch(error => {});
}, [lottieUrl]);
const getCachePath = () => {
let localPath = '';
const lottieObj = lottieDetails?.find(
lottie => lottie?.lottieFileName === lottieName
);
if (lottieObj) {
localPath = lottieObj?.lottieLocalPath;
}
return localPath;
};
return url ? (
<LottieView
source={url}
style={[props.style](http://props.style)}
autoPlay={props.autoPlay}
loop={props.loop}
resizeMode={props.resizeMode}
/>
) : (
<Image source={props.imgSource} style={props.imgStyle} />
);
};
export default LottieWrapper;*
If the Lottie animation is not downloaded by the time the component is rendered, an image will be shown. Once the download is complete, the image will be replaced with the Lottie animation.
Why Use readDownloadedLottie?
The readDownloadedLottie function is used to read Lottie files because Lottie doesn’t show directly from the local cache path. It needs to be read in UTF-8 format. Here is the function:
export const readDownloadedLottie = (downloadPath, callbackFunc) => {
RNFetchBlob.fs.readFile(downloadPath, 'utf8').then(data => {
const val = JSON.parse(data);
callbackFunc(val);
});
};
To use the wrapper class, create a component and pass all the required props:
<LottieWrapper
lottieUrl={LOTTIE_URL_PATH_DEFINED_IN_YOUR_APP}
style={styles.lottieStyle}
autoPlay={true}
loop={false}
imgSource={IMG_URL_OR_LOCAL_PATH_DEFINED_IN_YOUR_APP}
imgStyle={styles.imageStyle}
/>
Video Caching
The approach we’ve used to implement Lottie caching can be extended to video caching as well. While my primary focus in this article is on Lottie caching and its benefits, I will explore the topic of video caching in detail in an upcoming blog. Stay tuned for insights on how to optimize video performance using similar techniques!
Conclusion
By implementing a wrapper class for caching, we can greatly improve the performance and maintainability of our React Native apps. As the famous software engineer Robert C. Martin once said, “Clean code is simple and direct. Clean code reads like well-written prose.”
Subscribe to my newsletter
Read articles from Deeksha Chaturvedi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by