TodoApp: Sample CRUD React Native Expo using Redux Toolkit
Halo teman-teman, kembali lagi kita ke tutorial react native, kali ini kita akan membahas redux menggunakan redux-toolkit, redux toolkit lebih simple dibandingkan dengan redux biasa.
Redux adalah library untuk menghandle state management di react native, terdapat berbagai state management lain seperti Context, Mobx, dan recoil.
1. App.js
App.js berisi bagian utama dari redux, yaitu provider yang terhubung dengan store dari redux.
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 untuk menampilkan button dengan Icon
import 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 untuk menampilkan spasi jarak antara 2 komponen
import 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
Module TodoApp adalah module halaman utama tempat menampung module AddTodo sama TodoList
import 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
Module AddTodo adalah form untuk menampilkan form tambah dan edit task.
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 menampilkan semua data task termasuk tombol Delete dan task yang selesai
import 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
Catatan:
Tambahan fungsi update, delete dan toggle selesai dari original
Referensi
https://snack.expo.dev/@roycechua/redux-react-native-sample-redux-toolkit-complete