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.
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>
);
}
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 Recoil
function 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 atoms
export 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:
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 path
import { 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 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