TodoApp: Sample CRUD React Native Expo using Redux Toolkit
Hello friends, back to the react native tutorial, this time we will discuss redux using redux-toolkit, redux toolkit is simpler than regular redux.
Redux is a library for handling state management in React Native, there are various other state management such as Context, Mobx, and Recoil.
1. App.js
App.js contains the main part of redux, which is the provider connected to the redux store.import * as React from 'react';
import { Provider } from 'react-redux';
import { store } from './src/redux/store';
import { TodoApp } from './src/module/todos/TodoApp';
export default function App() {
return (
<Provider store={store}>
<TodoApp />
</Provider>
);
}
2. Component ButtonIcon
Component ButtonIcon to display button with Iconimport React from 'react';
import { Text, StyleSheet, TouchableOpacity } from 'react-native';
import { FontAwesome } from '@expo/vector-icons';
const ButtonIcon = (props) => {
const {
iconName = 'exclamation-circle',
size = 24,
color = 'black',
onPress,
label,
} = props;
return (
<TouchableOpacity style={styles.buttonStyle} onPress={onPress}>
<FontAwesome name={iconName} size={size} color={color} />
<Text style={styles.buttonLabelStyle}>{label}</Text>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
buttonStyle: {
margin: 10,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
padding: 5,
},
buttonLabelStyle: {
margin: 5,
},
});
export default ButtonIcon;
3. Component Spacer
Component Spacer to display the space between 2 componentsimport React from 'react';
import { View, StyleSheet } from 'react-native';
const Spacer = () => {
return <View style={styles.container} />;
};
const styles = StyleSheet.create({
container: {
marginVertical: 15,
},
});
export default Spacer;
4. Module TodoApp
The Todo App module is the main page module that houses the AddTodo and TodoList modulesimport React, { useState } from 'react';
import { Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
import Spacer from '../../components/Spacer';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
import { TodoList } from './TodoList';
import { AddTodo } from './AddTodo';
export const TodoApp = () => {
// variable to save edit item
const [updateItem, setUpdateItem] = useState();
return (
<View style={styles.container}>
<Card title="Card Title">
<Text style={styles.paragraph}>
ToDo App with React Native and Redux Toolkit
</Text>
</Card>
<Spacer />
<AddTodo updateItem={updateItem} setUpdateItem={setUpdateItem} />
<Spacer />
<TodoList updateItem={updateItem} setUpdateItem={setUpdateItem} />
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 10,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
5. Module AddTodo
The AddTodo module is a form to display the add and edit task forms.import React, { useState } from 'react';
import { View, Alert } from 'react-native';
import { useDispatch } from 'react-redux';
import { addTodo, updateTodo } from './../../redux/todosSlice';
import { Title, Card, TextInput, Button } from 'react-native-paper';
// You can import from local files
import Spacer from '../../components/Spacer';
export const AddTodo = ({ updateItem, setUpdateItem }) => {
const [text, setText] = useState();
const dispatch = useDispatch();
function handleSumbit() {
if (!updateItem) {
if (!text) {
Alert.alert('Info', 'Please entry the text field');
return;
}
dispatch(addTodo(text));
setText('');
setUpdateItem();
} else {
dispatch(updateTodo(updateItem));
setText('');
setUpdateItem();
}
}
return (
<View>
<Card>
<Card.Content>
{!updateItem ? (
<Title>Add ToDo Here</Title>
) : (
<Title>Edit ToDo Here</Title>
)}
<TextInput
mode="outlined"
label="Task"
value={!updateItem ? text : updateItem?.text}
onChangeText={(textdata) => {
!updateItem
? setText(textdata)
: setUpdateItem({ ...updateItem, text: textdata });
}}
/>
<Spacer />
<Button mode="contained" onPress={handleSumbit}>
{!updateItem ? `Add Task` : `Edit Task`}
</Button>
</Card.Content>
</Card>
</View>
);
};
6. Module TodoList
TodoList displays all task data including the Delete button and completed tasksimport React from 'react';
import {
View,
FlatList,
TouchableOpacity,
} from 'react-native';
// You can import from local files
import Spacer from '../../components/Spacer';
import ButtonIcon from '../../components/ButtonIcon';
// or any pure javascript modules available in npm
import { Paragraph, Card } from 'react-native-paper';
import { FontAwesome as Icon } from '@expo/vector-icons';
import { useDispatch, useSelector } from 'react-redux';
import { deleteTodo, toggleTodo } from './../../redux/todosSlice';
export function TodoList({ setUpdateItem }) {
const todos = useSelector((state) => state.todos);
const dispatch = useDispatch();
function handleDelete(id) {
dispatch(deleteTodo(id));
}
function handleUpdateStart(item) {
setUpdateItem(item);
}
function handleToogle(item) {
dispatch(toggleTodo(item));
}
return (
<View>
<FlatList
data={todos}
keyExtractor={(item) => item.id}
renderItem={({ item, index }) => {
return (
<>
<Card>
<Card.Title
title={`Task#${item.id}`}
left={(props) => (
<Icon name="tasks" size={24} color="black" />
)}
right={(props) => (
<ButtonIcon
iconName="close"
color="red"
onPress={() => handleDelete(item.id)}
/>
)}
/>
<Card.Content
style={{
flexDirection: 'row',
justifyContent: 'space-between',
}}>
<Paragraph>
<TouchableOpacity onPress={() => handleUpdateStart(item)}>
{item.text}
</TouchableOpacity>
</Paragraph>
<TouchableOpacity onPress={() => handleToogle(item)}>
<Icon
name="check"
size={22}
color={item.completed ? 'green' : 'gray'}
/>
</TouchableOpacity>
</Card.Content>
</Card>
<Spacer />
</>
);
}}
/>
</View>
);
}
6. Redux Store
import { configureStore } from '@reduxjs/toolkit';
import todoSlice from './todosSlice';
export const store = configureStore({
reducer: {
todos: todoSlice,
},
});
7. Redux todosSlice
import { createSlice } from '@reduxjs/toolkit';
let nextTodoId = 1;
const todosSlice = createSlice({
name: 'todos',
initialState: [],
reducers: {
addTodo(state, action) {
state.push({ id: nextTodoId++, text: action.payload, completed: false });
},
updateTodo(state, action) {
const todo = state.find((todo) => todo.id === action.payload.id);
if (todo) {
todo.text = action.payload.text;
}
},
deleteTodo(state, action) {
return state.filter((todo) => todo.id !== action.payload);
},
toggleTodo(state, action) {
const todo = state.find((todo) => todo.id === action.payload.id);
if (todo) {
todo.completed = !todo.completed;
}
},
},
});
export const { addTodo, updateTodo, deleteTodo, toggleTodo } =
todosSlice.actions;
export default todosSlice.reducer;
Source code:
https://snack.expo.dev/@rudiahmad/todoapp—sample-crud-with-redux-toolkit
https://github.com/rudiahmad/todoapp—sample-crud-with-redux-toolkit
Note:
Additional update, delete and toggle functions completed from the original
Reference
https://snack.expo.dev/@roycechua/redux-react-native-sample-redux-toolkit-complete