Chapter 14: Expo React Native State Management with Hooks - useState and useEffect
In this chapter, we'll dive deeper into React Native state management using the useState and useEffect hooks. These hooks are fundamental for handling local state and side effects in functional components. We'll explore their usage individually and how they can be combined to create powerful, interactive components.
1. Managing Local State with useState
The useState hook allows you to add state to functional components. It is used to declare state variables and provides a mechanism to update them.
Key Points:
- Syntax:
const [state, setState] = useState(initialState); - State Update Function: The function returned by
useStateis used to update the state, which triggers a re-render of the component. - Multiple State Variables: You can have multiple
useStatecalls to manage different pieces of state separately.
Example: Basic Counter with useState
import React, { useState } from "react";
import { View, Text, Button, StyleSheet } from "react-native";
export default function Counter() {
// Initialize a state variable `count` with initial value 0
const [count, setCount] = useState(0);
return (
<View style={styles.container}>
<Text style={styles.text}>Count: {count}</Text>
<Button title="Increase" onPress={() => setCount(count + 1)} />
<Button title="Decrease" onPress={() => setCount(count - 1)} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
text: {
fontSize: 24,
marginBottom: 20,
},
});
Explanation:
countis the state variable, andsetCountis the function to updatecount.- Each button press calls
setCount, which updates the state and re-renders the component with the new state value.
2. Side Effects with useEffect
The useEffect hook allows you to perform side effects in your components, such as data fetching, subscriptions, or manually changing the DOM.
Key Points:
- Syntax:
useEffect(() => { /* side effect */ }, [dependencies]); - Dependencies Array: Determines when the effect should re-run. If omitted, the effect runs after every render. If an empty array is passed, the effect only runs once (on mount and unmount).
- Cleanup Function: Optional return function for cleanup (like removing event listeners or canceling API requests) when the component is unmounted or before the effect re-runs.
Example: Fetch Data with useEffect
import React, { useState, useEffect } from "react";
import { View, Text, ActivityIndicator, StyleSheet } from "react-native";
export default function DataFetcher() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Fetch data when component mounts
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then((response) => response.json())
.then((json) => {
setData(json);
setLoading(false);
})
.catch((error) => console.error(error));
}, []); // Empty dependency array ensures this effect runs only once
return (
<View style={styles.container}>
{loading ? (
<ActivityIndicator size="large" color="#0000ff" />
) : (
<Text style={styles.text}>{data.title}</Text>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
text: {
fontSize: 18,
textAlign: "center",
margin: 10,
},
});
Explanation:
useEffectfetches data when the component mounts and sets the loading state tofalseonce data is fetched.- The empty dependency array
[]ensures that the fetch operation is only performed once, similar to thecomponentDidMountlifecycle method in class components.
3. Combining Both useEffect and useState
Combining useState and useEffect is a common pattern for managing state and side effects together. This allows for more dynamic and responsive components.
Example: Search Filter with useState and useEffect
import React, { useState, useEffect } from "react";
import { View, Text, TextInput, FlatList, StyleSheet } from "react-native";
export default function SearchFilter() {
const [query, setQuery] = useState("");
const [filteredData, setFilteredData] = useState([]);
const [data, setData] = useState([
{ id: "1", name: "Apple" },
{ id: "2", name: "Banana" },
{ id: "3", name: "Cherry" },
{ id: "4", name: "Date" },
{ id: "5", name: "Elderberry" },
]);
useEffect(() => {
// Filter data whenever query changes
setFilteredData(
data.filter((item) =>
item.name.toLowerCase().includes(query.toLowerCase())
)
);
}, [query]); // Re-run this effect whenever `query` changes
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="Search..."
value={query}
onChangeText={setQuery}
/>
<FlatList
data={filteredData}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <Text style={styles.item}>{item.name}</Text>}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
input: {
height: 40,
borderColor: "gray",
borderWidth: 1,
marginBottom: 20,
paddingHorizontal: 10,
},
item: {
padding: 10,
fontSize: 18,
},
});
Explanation:
useStatemanagesquery,filteredData, anddata.useEffectupdatesfilteredDatawheneverquerychanges.- This combination allows for a reactive search feature that filters the list based on user input.
4. Advanced Usage of useEffect and useState
To master state management with useEffect and useState, you need to understand their advanced uses, such as handling component unmounting and multiple side effects.
Advanced Example: Polling Data with Cleanup
import React, { useState, useEffect } from "react";
import { View, Text, Button, StyleSheet } from "react-native";
export default function PollingExample() {
const [data, setData] = useState(null);
const [isPolling, setIsPolling] = useState(false);
useEffect(() => {
let interval;
if (isPolling) {
interval = setInterval(() => {
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then((response) => response.json())
.then((json) => setData(json))
.catch((error) => console.error(error));
}, 5000); // Poll every 5 seconds
}
return () => {
if (interval) clearInterval(interval); // Cleanup on unmount or when isPolling changes
};
}, [isPolling]); // Re-run effect whenever `isPolling` changes
return (
<View style={styles.container}>
<Text style={styles.text}>{data ? data.title : "No data"}</Text>
<Button
title={isPolling ? "Stop Polling" : "Start Polling"}
onPress={() => setIsPolling(!isPolling)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
text: {
fontSize: 18,
textAlign: "center",
margin: 10,
},
});
Explanation:
useStatemanagesdataandisPolling.useEffecthandles the side effect of polling data from an API every 5 seconds whenisPollingistrue.- Cleanup Function: Clears the interval when the component unmounts or
isPollingchanges.
By combining useState and useEffect, you can manage both local state and side effects effectively in your Expo React Native application. This chapter provides the foundation to handle most state management scenarios using these hooks, enabling you to create responsive and dynamic mobile applications.