Redux Toolkit
Learn to implement Redux Toolkit for complex state management in React Native applications with modern Redux patterns. This is a foundational concept in cross-platform mobile development that professional developers rely on daily. The explanations below are written to be beginner-friendly while covering the depth and nuance that comes from real-world React Native experience. Take your time with each section and practice the examples
Redux Toolkit Overview
Redux Toolkit (RTK) is the official, opinionated, batteries-included toolset for efficient Redux development. It simplifies Redux setup and reduces boilerplate code significantly.. This is an essential concept that every React Native developer must understand thoroughly. In professional development environments, getting this right can mean the difference between code that works reliably and code that breaks in production. The following sections break this down into clear, digestible pieces with practical examples you can try immediately
RTK Features
- configureStore() - Simplified store setup — a critical concept in cross-platform mobile development that you will use frequently in real projects
- createSlice() - Reducers and actions in one place — a critical concept in cross-platform mobile development that you will use frequently in real projects
- createAsyncThunk() - Async action creators — a critical concept in cross-platform mobile development that you will use frequently in real projects
- createEntityAdapter() - Normalized state management
- RTK Query - Data fetching and caching — a critical concept in cross-platform mobile development that you will use frequently in real projects
Installing Redux Toolkit
Install Redux Toolkit and React Redux: `npm install @reduxjs/toolkit react-redux`. For React Native, you might also need `react-redux` with proper setup.. This is an essential concept that every React Native developer must understand thoroughly. In professional development environments, getting this right can mean the difference between code that works reliably and code that breaks in production. The following sections break this down into clear, digestible pieces with practical examples you can try immediately
Redux Toolkit Example
import React from 'react';
import { Provider, useSelector, useDispatch } from 'react-redux';
import { configureStore, createSlice } from '@reduxjs/toolkit';
import { View, Text, TouchableOpacity, StyleSheet, FlatList } from 'react-native';
// Counter Slice
const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0,
history: [],
},
reducers: {
increment: (state) => {
state.value += 1;
state.history.push({ action: 'increment', timestamp: Date.now() });
},
decrement: (state) => {
state.value -= 1;
state.history.push({ action: 'decrement', timestamp: Date.now() });
},
reset: (state) => {
state.value = 0;
state.history.push({ action: 'reset', timestamp: Date.now() });
},
clearHistory: (state) => {
state.history = [];
},
},
});
// Todo Slice
const todoSlice = createSlice({
name: 'todos',
initialState: {
items: [],
filter: 'all', // all, active, completed
},
reducers: {
addTodo: (state, action) => {
const newTodo = {
id: Date.now(),
text: action.payload,
completed: false,
createdAt: Date.now(),
};
state.items.push(newTodo);
},
toggleTodo: (state, action) => {
const todo = state.items.find(item => item.id === action.payload);
if (todo) {
todo.completed = !todo.completed;
}
},
deleteTodo: (state, action) => {
state.items = state.items.filter(item => item.id !== action.payload);
},
setFilter: (state, action) => {
state.filter = action.payload;
},
},
});
// Configure Store
const store = configureStore({
reducer: {
counter: counterSlice.reducer,
todos: todoSlice.reducer,
},
});
// Export actions
export const { increment, decrement, reset, clearHistory } = counterSlice.actions;
export const { addTodo, toggleTodo, deleteTodo, setFilter } = todoSlice.actions;
// Counter Component
const Counter = () => {
const count = useSelector(state => state.counter.value);
const history = useSelector(state => state.counter.history);
const dispatch = useDispatch();
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Counter: {count}</Text>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={styles.button}
onPress={() => dispatch(increment())}
>
<Text style={styles.buttonText}>+</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => dispatch(decrement())}
>
<Text style={styles.buttonText}>-</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.resetButton]}
onPress={() => dispatch(reset())}
>
<Text style={styles.buttonText}>Reset</Text>
</TouchableOpacity>
</View>
<Text style={styles.historyTitle}>History ({history.length})</Text>
<FlatList
data={history.slice(-5).reverse()}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => (
<Text style={styles.historyItem}>
{item.action} at {new Date(item.timestamp).toLocaleTimeString()}
</Text>
)}
style={styles.historyList}
/>
{history.length > 0 && (
<TouchableOpacity
style={styles.clearButton}
onPress={() => dispatch(clearHistory())}
>
<Text style={styles.clearButtonText}>Clear History</Text>
</TouchableOpacity>
)}
</View>
);
};
// Todo Component
const TodoApp = () => {
const todos = useSelector(state => state.todos.items);
const filter = useSelector(state => state.todos.filter);
const dispatch = useDispatch();
const filteredTodos = todos.filter(todo => {
if (filter === 'active') return !todo.completed;
if (filter === 'completed') return todo.completed;
return true;
});
const addNewTodo = () => {
const text = `Todo ${todos.length + 1}`;
dispatch(addTodo(text));
};
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Todo App</Text>
<View style={styles.filterContainer}>
<TouchableOpacity
style={[styles.filterButton, filter === 'all' && styles.activeFilter]}
onPress={() => dispatch(setFilter('all'))}
>
<Text style={styles.filterText}>All</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.filterButton, filter === 'active' && styles.activeFilter]}
onPress={() => dispatch(setFilter('active'))}
>
<Text style={styles.filterText}>Active</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.filterButton, filter === 'completed' && styles.activeFilter]}
onPress={() => dispatch(setFilter('completed'))}
>
<Text style={styles.filterText}>Completed</Text>
</TouchableOpacity>
</View>
<TouchableOpacity style={styles.addButton} onPress={addNewTodo}>
<Text style={styles.addButtonText}>Add Todo</Text>
</TouchableOpacity>
<FlatList
data={filteredTodos}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => (
<View style={styles.todoItem}>
<TouchableOpacity
style={styles.todoText}
onPress={() => dispatch(toggleTodo(item.id))}
>
<Text style={[
styles.todoTextContent,
item.completed && styles.completedTodo
]}>
{item.text}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.deleteButton}
onPress={() => dispatch(deleteTodo(item.id))}
>
<Text style={styles.deleteButtonText}>×</Text>
</TouchableOpacity>
</View>
)}
style={styles.todoList}
/>
</View>
);
};
// Main App
const App = () => {
return (
<Provider store={store}>
<View style={styles.container}>
<Text style={styles.title}>Redux Toolkit Example</Text>
<Counter />
<TodoApp />
</View>
</Provider>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 30,
color: '#333',
},
section: {
marginBottom: 30,
backgroundColor: 'white',
padding: 20,
borderRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
sectionTitle: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 15,
color: '#333',
},
buttonContainer: {
flexDirection: 'row',
marginBottom: 15,
},
button: {
backgroundColor: '#2196F3',
padding: 10,
marginHorizontal: 5,
borderRadius: 5,
minWidth: 50,
alignItems: 'center',
},
resetButton: {
backgroundColor: '#f44336',
},
buttonText: {
color: 'white',
fontWeight: 'bold',
},
historyTitle: {
fontSize: 16,
fontWeight: 'bold',
marginBottom: 10,
color: '#333',
},
historyList: {
maxHeight: 100,
},
historyItem: {
fontSize: 12,
color: '#666',
marginBottom: 2,
},
clearButton: {
backgroundColor: '#ff9800',
padding: 8,
borderRadius: 5,
alignItems: 'center',
marginTop: 10,
},
clearButtonText: {
color: 'white',
fontSize: 12,
fontWeight: 'bold',
},
filterContainer: {
flexDirection: 'row',
marginBottom: 15,
},
filterButton: {
padding: 8,
marginRight: 10,
borderRadius: 5,
backgroundColor: '#e0e0e0',
},
activeFilter: {
backgroundColor: '#2196F3',
},
filterText: {
fontSize: 14,
color: '#333',
},
addButton: {
backgroundColor: '#4CAF50',
padding: 10,
borderRadius: 5,
alignItems: 'center',
marginBottom: 15,
},
addButtonText: {
color: 'white',
fontWeight: 'bold',
},
todoList: {
maxHeight: 200,
},
todoItem: {
flexDirection: 'row',
alignItems: 'center',
padding: 10,
borderBottomWidth: 1,
borderBottomColor: '#e0e0e0',
},
todoText: {
flex: 1,
},
todoTextContent: {
fontSize: 16,
color: '#333',
},
completedTodo: {
textDecorationLine: 'line-through',
color: '#999',
},
deleteButton: {
backgroundColor: '#f44336',
width: 30,
height: 30,
borderRadius: 15,
alignItems: 'center',
justifyContent: 'center',
},
deleteButtonText: {
color: 'white',
fontSize: 18,
fontWeight: 'bold',
},
});
export default App;