React Native

Managing State with Recoil and Recoil Persist in React Native: A Comprehensive Guide


Introduction

Recoil is a powerful state management library for React applications, including React Native. Though persistent Recoil state is not natively supported by React Native, we can work around this using AsyncStorage. This tutorial will guide you through step-by-step installation of Recoil, implementation of state management, and manual persistence using AsyncStorage.

Why Use Recoil?

Recoil is simple yet powerful. It enables shared state management between components while maintaining the performance and simplicity developers love in React. Its atomic architecture allows precise and predictable state updates.

Installation Steps

1. Install Recoil: Run the following command to add Recoil to your project:

npm install recoil 

2. Install AsyncStorage: For state persistence, add AsyncStorage to your project:

npm install @react-native-async-storage/async-storage

 

3. Link AsyncStorage: For React Native versions below 0.60, you must link the library manually:

react-native link @react-native-async-storage/async-storage

 

4. Wrap Your App in RecoilRoot: The RecoilRoot component initializes Recoil and is required for managing state. Your App.js should look like this:

import React from 'react';import { NavigationContainer } from '@react-navigation/native';import { RecoilRoot } from 'recoil';import MainNavigation from './src/Navigation/MainNavigation';export default function App() { return ( <RecoilRoot> <NavigationContainer> <MainNavigation /> </NavigationContainer> </RecoilRoot> );}

 

State Management with Recoil

Recoil uses atoms to represent shared state. Here’s how we define Recoil atoms:

Atom with Persistence

Recoil is based on the usage of atoms that represent shared, reactive state in an application. Atoms are defined with a unique key using the atom function along with an initial default value. Components subscribe to an atom; thus, changes in the atom's state lead to re-rendering and therefore efficient, predictable updates.

import { atom } from 'recoil';import AsyncStorage from '@react-native-async-storage/async-storage';// Custom persistence effect for Recoilfunction persistAtom(key) { return ({ setSelf, onSet, trigger }) => { const loadPersisted = async () => { const savedValue = await AsyncStorage.getItem(key); if (savedValue != null) { try { setSelf(JSON.parse(savedValue)); } catch (error) { console.error("Error parsing stored value for key:", key, error); // Handle the error, e.g., set a default value or remove the invalid data AsyncStorage.removeItem(key); // Example: Remove invalid data } } }; loadPersisted(); if (trigger === 'get') { onSet((newValue, isReset) => { isReset ? AsyncStorage.removeItem(key) : AsyncStorage.setItem(key, JSON.stringify(newValue)); }); } };}// Defining atomsexport const Products = atom({ key: 'Products', default: [], effects_UNSTABLE: [persistAtom('MyProducts')],});export const LikedItems = atom({ key: 'LikedItems', default: [], effects_UNSTABLE: [persistAtom('MyLikedItems')],});export const CartItems = atom({ key: 'CartItems', default: [], effects_UNSTABLE: [persistAtom('MyCartItems')],});

 

Key Points:

  • Persistence Effect: The persistAtom function integrates AsyncStorage with Recoil for persistence.
  • effects_UNSTABLE: This API allows custom behaviors like persisting data to AsyncStorage.

Product Details Component

Here’s an example of how to use Recoil state in a React Native component:

import React, { useEffect, useState } from 'react';import { FlatList, Image, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';import { useRecoilState } from 'recoil';import { CartItems, Products } from './recoil/Atoms'; // Corrected import pathimport { productData } from './constant/productData';const ProductDetails = ({ navigation }) => { const [productList, setProductList] = useRecoilState(Products); const [cartList, setCartList] = useRecoilState(CartItems); const [searchText, setSearchText] = useState(''); const [searchData, setSearchData] = useState([]); useEffect(() => { setProductList(productData); // Initialize product list from data }, []); useEffect(() => { let timer; if (searchText.length > 0) { timer = setTimeout(() => { setSearchData(searchFromProductData(searchText)); }, 500); // Debounce search by 500ms } else { setSearchData([]); } return () => clearTimeout(timer); // Clear timeout on unmount or searchText change }, [searchText]); const searchFromProductData = (search) => { return productList.filter((item) => item.name.toLowerCase().includes(search.toLowerCase()) ); }; return ( <View style={styles.mainContainer}> <View style={styles.searchBox}> <TextInput style={{ height: 40, width: '90%', padding: 10 }} // Added padding for better UX placeholder="Search Product by name" value={searchText} onChangeText={setSearchText} /> </View> <FlatList data={searchText.length > 0 ? searchData : productList} // Conditional rendering renderItem={({ item }) => ( <View style={styles.productContainer}> <Image source={require('./constant/images/smartphone.png')} // Corrected image path style={styles.productImage} /> <Text style={styles.productName}>{item.name}</Text> </View> )} numColumns={2} keyExtractor={(item, index) => index.toString()} // Use index as key if no unique ID /> </View> );};const styles = StyleSheet.create({ mainContainer: { flex: 1, padding: 16, }, searchBox: { margin: 16, padding: 8, borderWidth: 1, borderRadius: 5, }, productContainer: { margin: 8, padding: 8, borderWidth: 1, borderRadius: 5, flex: 1, // Important for even distribution in numColumns }, productImage: { width: '100%', height: 100, resizeMode: 'contain', }, productName: { marginTop: 5, fontSize: 16, },});export default ProductDetails;

 

Key Considerations

  • Recoil Persistence Limitation: Recoil does not support persistence natively. The effects_UNSTABLE API and AsyncStorage work as a manual workaround.
  • Custom Persistence Logic: You can adapt the persistAtom function for other storage solutions if needed.

 

Conclusion

Recoil simplifies state management in React Native. By using AsyncStorage for persistence, you can persist state even after app restarts. With this setup, your app benefits from the powerful state management of Recoil and the persistence of AsyncStorage.

 

Ready to transform your business with our technology solutions? Contact Us today to Leverage Our React Native Expertise.

0

React Native

Related Center Of Excellence