Redux Toolkit을 사용할 것이고 Redux 스토어에서 관리되는 상태 및 상태 변경을 정의하는 Redux 슬라이스(slice)를 먼저 생성합니다.
todoSlice.js
import { createSlice } from "@reduxjs/toolkit";
// 1
const todoSlice = createSlice({
name: "todo",
initialState: {
currentId: 4,
todos: [],
},
// 2
reducers: {
addTodo: (state, action) => {
state.todos.push({
id: state.currentId++,
text: action.payload.trim(),
state: "todo", // 'todo' or 'done'
});
},
updateTodo: (state, action) => {
const item = state.todos.findIndex((item) => item.id === action.payload);
state.todos[item].state =
state.todos[item].state === "todo" ? "done" : "todo";
state.todos.push(state.todos.splice(item, 1)[0]);
},
deleteTodo: (state, action) => {
const item = state.todos.findIndex((item) => item.id === action.payload);
// index가 -1이면 해당 인덱스가 없음 -> 조건 추가
if (item > -1) {
state.todos.splice(item, 1);
}
},
},
});
// 3
export default todoSlice.reducer;
export const { addTodo, updateTodo, deleteTodo } = todoSlice.actions;
- Redux 슬라이스를 생성합니다. 이 슬라이스는 상태(state)와 해당 상태를 변경하는 리듀서(reducer) 함수를 포함하며 이 슬라이스를 생성할 때, 여러 옵션을 지정할 수 있으며, 일단 `name`과 `initialState`를 설정하였습니다.
- 슬라이스에 포함된 리듀서(reducer) 함수들을 정의한 객체입니다. 리듀서 함수는 상태를 변경하는 역할을 하며 이 슬라이스에서는 세 가지 리듀서 함수를 정의하였습니다.
- `addTodo`: 새로운 할 일 항목을 todos 배열에 추가하는 역할을 하며 `action.payload`에는 추가할 텍스트가 포함됩니다.
- `updateTodo`: 특정 ID를 가진 할 일 항목의 상태를 토글(완료 <-> 미완료)하는 역할을 합니다.
- `deleteTodo`: 특정 ID를 가진 할 일 항목을 todos 배열에서 제거하는 역할을 합니다.
- 슬라이스를 내보내고 리듀서 함수 및 액션 생성자(action creator) 함수들을 내보냅니다.
- `export default todoSlice.reducer`: 슬라이스의 리듀서 함수를 내보내며 이것은 Redux 스토어에서 사용됩니다.
- `export const { addTodo, updateTodo, deleteTodo } = todoSlice.actions`: 액션 생성자 함수들을 내보내며 이러한 함수들은 Redux 액션을 생성하는 데 사용되며, dispatch를 통해 리듀서에 전달됩니다.
이제 Redux 상태를 보관하고 앱에서 상태 관리를 하기 위한 중심 역할을 할 Redux 스토어를 설정해 줍니다.
store.js
import { configureStore } from "@reduxjs/toolkit";
import todoReducer from "./slices/todoSlice";
// 1
export const store = configureStore({
// 2
reducer: {
todo: todoReducer,
},
});
- Redux 스토어를 생성하고 이 스토어를 사용하여 Redux의 상태를 관리하고 앱의 상태를 변경할 수 있습니다.
- Redux 스토어의 reducer 설정 부분에서는 스토어에 포함될 리듀서(reducer) 함수를 설정하며 이 코드에서는 `todoReducer`를 "todo" 슬라이스에 연결합니다.
- `todoReducer`: todoReducer는 이전에 정의한 todoSlice에서 내보낸 리듀서 함수이며 "todo" 슬라이스와 관련된 상태 변경을 처리합니다.
- reducer 설정은 여러 개의 리듀서를 결합하여 사용할 수 있으며, 각 리듀서가 다른 슬라이스의 state를 관리하게 됩니다.
이제 Redux 스토어를 사용하여 앱의 전역 상태를 관리하는 데 필요한 환경을 설정합니다.
App.js
import React from "react";
import { StyleSheet } from "react-native";
import { Provider } from "react-redux";
import MainScreen from "./screens/MainScreen";
import { store } from "./redux/store";
export default function App() {
return (
<Provider store={store}>
<MainScreen />
</Provider>
);
}
const styles = StyleSheet.create({});
앱의 진입점에서 Redux 스토어를 초기화하고 Provider 컴포넌트로 래핑하여 모든 하위 컴포넌트에서 Redux state를 사용할 수 있도록 설정합니다.
이제 Redux 스토어에서 state를 선택하고 가져올 겁니다.
MainScreen.js
import React from "react";
import { FlatList, SafeAreaView, StyleSheet, Text, View } from "react-native";
import { StatusBar } from "expo-status-bar";
import { useSelector } from "react-redux";
import InputForm from "../components/InputForm";
import TodoItem from "../components/TodoItem";
const MainScreen = () => {
// 1
const todos = useSelector((state) => state.todo.todos);
const todoTasks = todos.filter((item) => item.state === "todo");
const completedTasks = todos.filter((item) => item.state === "done");
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle={"default"} />
<Text style={styles.pageTitle}>오늘 할 일</Text>
<View style={styles.listView}>
<Text style={styles.listTitle}>목록</Text>
{todoTasks.length !== 0 ? (
<FlatList // 2
data={todoTasks}
renderItem={({ item }) => <TodoItem {...item} />}
keyExtractor={(item) => item.id}
/>
) : (
<Text style={styles.emptyListText}>할 일 없음</Text>
)}
</View>
<View style={styles.separator} />
<View style={styles.listView}>
<Text style={styles.listTitle}>완료</Text>
{completedTasks.length !== 0 ? (
<FlatList
data={completedTasks}
renderItem={({ item }) => <TodoItem {...item} />}
keyExtractor={(item) => item.id}
/>
) : (
<Text style={styles.emptyListText}>완료된 일 없음</Text>
)}
</View>
<InputForm />
</SafeAreaView>
);
};
export default MainScreen;
- `useSelector`를 사용하여 Redux 스토어에서 state를 선택하고 todos 배열을 "todo" 상태와 "done" 상태로 나누어 todoTasks와 completedTasks 배열을 생성합니다.
- 먼저 데이터의 유무에 따라 조건부 렌더링을 설정해 줍니다. 그리고 사용할 데이터를 FlatList를 통해 뿌려줍니다. 여기서 React Native 코어 컴포넌트 중 데이터를 리스트로 뿌리는 건 두 가지 방법 `<FlatList>`와 `<ScrollView>`가 존재합니다. 여러 자잘한 차이점이 있지만 가장 핵심인 점은 아래와 같습니다.
FlatList | 아이템들을 필요할 때만 렌더링하므로 대규모 목록을 처리할 때 효율적입니다. 스크롤 영역 내에 표시되는 아이템만 렌더링하고, 스크롤 영역 밖의 아이템은 렌더링되지 않습니다. 이로 인해 성능이 최적화되고 메모리 사용량이 낮아집니다. |
ScrollView | 모든 자식 요소를 한 번에 렌더링하므로 대규모 목록을 다룰 때 성능이 저하될 수 있습니다. 모든 아이템을 메모리에 보유하므로 메모리 사용량이 증가할 수 있습니다. |
마지막으로 state를 바꾸게 할 겁니다.
InputForm.js
import React, { useState } from "react";
import {
StyleSheet,
Text,
KeyboardAvoidingView,
TextInput,
Pressable,
} from "react-native";
import { useDispatch } from "react-redux";
import { addTodo } from "../redux/slices/todoSlice";
const InputForm = () => {
const [currentValue, setCurrentValue] = useState("");
const dispatch = useDispatch();
// 1
const handleSubmit = () => {
if (currentValue !== "") {
dispatch(addTodo(currentValue));
setCurrentValue("");
}
};
return (
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : "height"}
style={styles.addFormContainer}
>
<TextInput
// 2
style={styles.inputField}
placeholder="할 일을 작성해주세요."
value={currentValue}
onChangeText={setCurrentValue}
onSubmitEditing={handleSubmit}
/>
<Pressable style={styles.addButton} onPress={handleSubmit} // 3>
<Text style={styles.addButtonText}>+</Text>
</Pressable>
</KeyboardAvoidingView>
);
};
export default InputForm;
- `addTodo` 액션을 Redux 스토어에 디스패치합니다. 이로써 입력된 할 일 항목이 Redux 스토어에 추가되고, 앱의 다른 부분에서 이 항목을 조회할 수 있습니다. 그리고 입력 후, 입력 필드를 비워주기 위해 초기화합니다.
- 입력된 텍스트는 `currentValue`에 저장되며, `onChangeText` 핸들러를 사용하여 입력 값이 변경될 때마다 `currentValue`를 업데이트합니다. 또, 사용자가 텍스트 입력 필드에서 엔터 키를 누를 때 호출되는 onSubmitEditing에도 `handleSubmit`을 호출하여할 일 항목이 추가됩니다.
- 사용자가 버튼을 누르면 `handleSubmit `함수가 호출되어 역시 할 일 항목이 추가됩니다.
'REACT-NATIVE' 카테고리의 다른 글
리액트 네이티브 07 - 파이어베이스 로그인 적용 (4) | 2023.11.14 |
---|---|
리액트 네이티브 06 - 라우터 설정 (0) | 2023.11.10 |
리액트 네이티브 00 - 번외(Git Bash) (6) | 2023.10.30 |
리액트 네이티브 04 - svg 사용하기 (4) | 2023.10.22 |
리액트 네이티브 03 - Todo App 구조 잡기 (5) | 2023.10.15 |