본문 바로가기
REACT-NATIVE

리액트 네이티브 05 - Redux 적용

by 일태찡 2023. 11. 3.

 

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;

 

    1. Redux 슬라이스를 생성합니다. 이 슬라이스는 상태(state)와 해당 상태를 변경하는 리듀서(reducer) 함수를 포함하며 이 슬라이스를 생성할 때, 여러 옵션을 지정할 수 있으며, 일단 `name`과 `initialState`를 설정하였습니다.
    2. 슬라이스에 포함된 리듀서(reducer) 함수들을 정의한 객체입니다. 리듀서 함수는 상태를 변경하는 역할을 하며 이 슬라이스에서는 세 가지 리듀서 함수를 정의하였습니다.
      • `addTodo`: 새로운 할 일 항목을 todos 배열에 추가하는 역할을 하며 `action.payload`에는 추가할 텍스트가 포함됩니다.
      • `updateTodo`: 특정 ID를 가진 할 일 항목의 상태를 토글(완료 <-> 미완료)하는 역할을 합니다.
      • `deleteTodo`: 특정 ID를 가진 할 일 항목을 todos 배열에서 제거하는 역할을 합니다.
    3. 슬라이스를 내보내고 리듀서 함수 및 액션 생성자(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,
	},
});

 

  1. Redux 스토어를 생성하고 이 스토어를 사용하여 Redux의 상태를 관리하고 앱의 상태를 변경할 수 있습니다.
  2. 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;

 

  1. `useSelector`를 사용하여 Redux 스토어에서 state를 선택하고 todos 배열을 "todo" 상태와 "done" 상태로 나누어 todoTaskscompletedTasks 배열을 생성합니다.
  2. 먼저 데이터의 유무에 따라 조건부 렌더링을 설정해 줍니다. 그리고 사용할 데이터를 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;

 

  1. `addTodo` 액션을 Redux 스토어에 디스패치합니다. 이로써 입력된 할 일 항목이 Redux 스토어에 추가되고, 앱의 다른 부분에서 이 항목을 조회할 수 있습니다. 그리고 입력 후, 입력 필드를 비워주기 위해 초기화합니다.
  2. 입력된 텍스트는 `currentValue`에 저장되며, `onChangeText` 핸들러를 사용하여 입력 값이 변경될 때마다 `currentValue`를 업데이트합니다. 또, 사용자가 텍스트 입력 필드에서 엔터 키를 누를 때 호출되는 onSubmitEditing에도 `handleSubmit`을 호출하여할 일 항목이 추가됩니다.
  3. 사용자가 버튼을 누르면 `handleSubmit `함수가 호출되어 역시 할 일 항목이 추가됩니다.