REDUX 개요 및 투두 만들면서 배워보기 1편

IT쿠키·2022년 8월 15일
0

[React-Redux]

목록 보기
1/1

리덕스 개요

코드를 시작하기 전에 Redux의 다른 부분과 함께 작동하는 방식을 살펴보겠습니다.
Redux는 action , reducers , state , store로 구성되어 있습니다. 각각은 하나의 특정 작업을 수행합니다. 예를 들어 보겠습니다.

버튼 클릭(위 다이어그램의 "UI" 상자)으로 약간의 돈을 입금할 수 있는 구성 요소가 있다고 가정해 보겠습니다.
버튼을 클릭하면 일반적으로 이 이벤트를 처리하는 함수를 호출합니다. 이것은 우리가 액션을 보낼 곳입니다.

Dispatch 는 Redux에서 제공한 기능으로 action을 트리거 할 수 있습니다.

작업 에는 유형 및 페이로드 가 포함 됩니다. 유형은 일반적으로 작업 이름이 있는 문자열일 뿐입니다. 페이로드에는 우리가 알아야 할 데이터가 포함되어 있습니다. 예를 들어, 금액을 모르면 돈을 입금할 수 없습니다.

store 는 action을 받고 state를 유지하는 역할을 한다. 모든 데이터를 한 곳에 보관한다는 의미에서 데이터베이스처럼 생각하십시오.
스토어는 또한 리듀서 를 사용하여 액션과 현재 상태를 기반으로 상태 업데이트를 담당합니다 .

리듀서 는 화려하게 들리지만 저장소에서 현재 상태와 작업을 가져오는 기능일 뿐입니다. 사물을 결합하고 새로운 상태를 반환합니다.
컨베이어 벨트처럼 생각하십시오. 이전 상태와 동작을 취하고 일부 작업을 수행하고 새 상태를 뱉어냅니다.
그런 다음 저장소는 감속기에서 반환된 이 새 상태를 저장하고 해당 상태를 구성 요소에 전달합니다. 이로 인해 다시 렌더링되어 새 데이터가 표시됩니다.

왜 이 모든 것이 필요합니까?

수백 또는 수천 개의 구성 요소가 있는 앱을 상상해 보십시오. 상태를 전달하고 어떤 구성 요소가 상태를 변경하는지, 어떻게 상태를 변경하는지 등을 기억하는 것은 다루기 어려워집니다.
이렇게 쪼개서 서로 다른 책임을 맡게 되고, 우리의 모든 상태를 한 곳에 보관하게 됩니다.
이것은 우리가 더 쉽게 이해하고 테스트하기 쉽게 만듭니다. 예를 들어, 리듀서는 순수 함수이기 때문에 별도로 테스트할 수 있습니다. 액션이 올바르게 전달되고 스토어가 상태를 올바르게 저장하는지 테스트할 수 있습니다.

스토어 구성 방법

Redux 툴킷을 사용하여 스토어를 시작으로 Redux를 작동시키는 데 필요한 모든 것을 설정할 것입니다.
redux 라는 src 폴더 에 새 폴더를 만듭니다 . 이 폴더 내에서 store.js 라는 파일을 만들고 다음 코드를 추가합니다.

import { configureStore } from '@reduxjs/toolkit';
export default configureStore({
	reducer: {},
});

configure store 기능은 우리를 위해 모든 힘든 일을 합니다. 상태를 유지하는 저장소를 만들고, 감속기를 결합하고, 나중에 사용할 멋진 내장 미들웨어를 포함합니다.
configureStore 함수는 내보낼 수 있는 저장소를 반환합니다(3행). 이것은 우리가 곧 할 우리의 앱에 스토어를 연결할 수 있게 해줍니다.
객체를 전달하여 수행하는 configureStore 함수에 감속기를 전달해야 합니다.
아직 리듀서를 만들지 않았지만 여기에서 원하는 만큼 리듀서를 가질 수 있습니다.

스토어를 앱에 연결하는 방법
우리에게는 스토어가 있고 앱을 여기에 결합을 해보자!

index.js를 열고 다음 항목을 추가해주자

import React from 'react';
import ReactDOM from 'react-dom';
//리액트 돔 연결
import './index.css';
import App from './App';
import store from './redux/store';
// 스토어 항목이 프로바이더 위에 있어야 한다
import { Provider } from 'react-redux';
// 리액트 프로바이더로 앱을 감싸준다.
ReactDOM.render(
	<React.StrictMode>
		<Provider store={store}>
			<App />
		</Provider>
	</React.StrictMode>,
	document.getElementById('root')
);

슬라이스를 만드는 방법

슬라이스는 데이터 조각 또는 슬라이스를 저장하는 방법을 제공을 해주고 해당 데이터를 변경하고 검색하는 데에 필요한 요건들을 제공해 준다.
데이터 베이스 테이블과 유사한 데이터 그룹이라고 생각을 해주자 일단은!

src/redux 폴더안에 todoSlice.js 라는 새 파일을 만들어주자

import { createSlice } from '@reduxjs/toolkit';

export const todoSlice = createSlice({
	name: 'todos',
	initialState: [
		{ id: 1, title: 'todo1', completed: false },
		{ id: 2, title: 'todo2', completed: false },
		{ id: 3, title: 'todo3', completed: true },
		{ id: 4, title: 'todo4', completed: false },
		{ id: 5, title: 'todo5', completed: false },
	],
	reducers: {
		addTodo: (state, action) => {
			const todo = {
				id: new Date(),
				title: action.payload.title,
				completed: false,
			};
			state.push(todo);
		},
		// addTodo :  => 투두 추가해주는 구문

	},
});


export const { addTodo } = todoSlice.actions;

export default todoSlice.reducer;

createSlice 함수는 우리에게 어떤 것을 return 을 돌려주고 그것을 todoslice 변수에 할당하는 것

이것은 우리가 내보낼 수 있는 액션과 리듀서를 얻는 곳입니다.

이 함수에 몇 가지 속성을 전달해야 올바른 결과를 얻을 수 있음 우리는 이것을 객체로 할당합니다.
먼저 슬라이스에 이름을 지정하고 todo의 슬라이스에 있으므로 이걸 todo라고 해줌

다음으로 initialState:초깃값을 설정을 해주고 더미 데이터를 추가 해준다.

이제 reducer를 추가한 후 reducer 작업에 응답하고 현재 상태를 취하고 작업을 payload를 기반으로 새 상태로 만들어 줍니다.

redux는 무대 뒤에서 상태와 동작을 전달합니다.
상태는 이 슬라이스의 현재 상태이며 작업에는 유형과 payload가 포함이 된다.

그래서 여기서 addTodo 액션을 디스패치할 때, 이것은 그 액션을 처리하는 것을 리듀서라 한다.

리듀서 내에서 상태를 업데이트 하는 논리를 수행하고 할일을 추가하기 때문에 가장 먼저 하고 싶은 일은 새로운 할일 객체를 만들어 주는 것 입니다. 날짜를 기반으로 id를 생성하여 고유한지 확인하고 페이로드에서 제목을 가져오고 기본적으로 완료됨을 false로 설정합니다.

이제 우리는 이것을 객체 상태로 푸시하고 이 시점에서 redux 는 새로운 상태를 디스패치로 취하고 저장소를 업데이트 한다.

액션과 리듀서를 내보내는 방법

구성 요소가 액세스 할 수 있도록 구조 제거를 사용하여 작업을 시행하고 내보낼 수도 있다.
그래서 todoslice는 우리의 리듀서 이름을 기반으로 많은 액션을 생성하고 그 후에 addTodo 액션을 가져오고 내보내기 위해 구조화를 사용했다.

스토어에 감속기를 추가하는 방법

이제 저장소에 리듀서를 추가해보자

import { configureStore } from '@reduxjs/toolkit';
import todoReducer from './todoSlice';

export default configureStore({
	reducer: {
		todos: todoReducer,
	},
});

store 모든 reducer 보유하고 관리를 하게 된다.
예를 들어 여기에 useReducer 라는 또 다른 reducer 가 있을 수 있으며, store는 모든 것을 처리한다.

새 할일을 추가하는 방법

우리는 리듀서와 할일을 추가하는 액션을 만든 후 액션을 디스패치를 하지 않았다.

addTodo 액션 디스패치

우리가 원하는 것은 사용자가 제출을 눌렀을 때 addTodo 액션을 전달하는 것입니다.
AddTodoForm.js 에서 사용자가 submit을 눌렀을 때 addTodo 액션을 디스패치하기를 원합니다.
다음 으로 AddTodoForm.js 의 코드를 업데이트하십시오 .

import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
// 액션을 호출함
import { addTodo } from '../redux/todoSlice';
// todoslice에 있는 타입을 호출

const AddTodoForm = () => {
	const [value, setValue] = useState('');
	const dispatch = useDispatch();

	const onSubmit = (event) => {
		event.preventDefault();
		if (value) {
			dispatch(
				addTodo({
					title: value,
				})
			);
		}
	};

	return (
		<form onSubmit={onSubmit} className='form-inline mt-3 mb-3'>
			<label className='sr-only'>Name</label>
			<input
				type='text'
				className='form-control mb-2 mr-sm-2'
				placeholder='Add todo...'
				value={value}
				onChange={(event) => setValue(event.target.value)}
			></input>

			<button type='submit' className='btn btn-primary mb-2'>
				Submit
			</button>
		</form>
	);
};

export default AddTodoForm;
  1. useDispatch 후크와 addTodo 작업을 가져오고
  2. 양식이 제출 될 때 호출되는 함수에서 addTodo 작업을 전달하는 것을 호출
  3. 각 할일에 대해 제목을 알아야 하므로 새 개체를 추가하고 제목을 전달

할 일 목록을 표시하는 방법

이제 디스패치 작업과 상태 업데이트를 살펴보았으므로 redux 에서 데이터를 검색하는 방법을 알아보자

TodoList 구성 요소에서 더미 목록을 사용하는 대신 Redux 에서 TODO를 가져오자

이를 위해 useSelector 후크를 사용해서 TodoList.js 를 열고 변경을 해보자

import React, { useEffect } from 'react';
import TodoItem from './TodoItem';
import { useSelector } from 'react-redux';
// useSelector 로 해당 상태값을 가져올 수 있음
const TodoList = () => {
	const todos = useSelector((state) => state.todos);

	return (
		<ul className='list-group'>
			{todos.map((todo) => (
				<TodoItem id={todo.id} title={todo.title} completed={todo.completed} />
			))}
		</ul>
	);
};

export default TodoList;

useSelector 후크는 함수를 수락하고 해당 함수를 기반으로 데이터를 반환합니다.
셀렉터 함수로 전달 받은 데이터 값을 state.todos를 수행을 하고 이것을 상점으로 이동하여 상태에서 모든 할일을 선택하고 이것을 우리가 정의한 todos 변수에 할당합니다.

이걸 실행하게 되면 todoSlice에서 todos를 가져오는 것을 볼 수 있습니다.
useSelector 후크 함수에 전달되는 상태 값은 Redux에 저장된 전체 상태 트리입니다.

상태 조각이 여러 개인 경우 전체를 반환한다.

할일을 완료로 표시하는 방법

감속기/액션 만들기

이제 할일을 완료로 표시하는 방법을 해보자 기존 상태를 업데이트 또한 포함이된다.

import { createSlice } from '@reduxjs/toolkit';

export const todoSlice = createSlice({
	name: 'todos',
	initialState: [
		{ id: 1, title: 'todo1', completed: false },
		{ id: 2, title: 'todo2', completed: false },
		{ id: 3, title: 'todo3', completed: true },
		{ id: 4, title: 'todo4', completed: false },
		{ id: 5, title: 'todo5', completed: false },
	],
	reducers: {
    	//addtodo 를 사용하여 
		addTodo: (state, action) => {
			const todo = {
				id: new Date(),
				title: action.payload.title,
				completed: false,
			};
			state.push(todo);
		},
        //
		toggleComplete: (state, action) => {
			const index = state.findIndex((todo) => todo.id === action.payload.id);
			state[index].completed = action.payload.completed;
		},
	},
});

export const { addTodo, toggleComplete } = todoSlice.actions;

export default todoSlice.reducer;

새로운 리듀서를 추가를 해주고 각 리듀서는 현재 상태와 리덕스에 의해 전달되는 작업이라고 생각을 해주자

구성 요소가 toggleComplete 작업을 전달하면 리듀서가 해당 작업을 처리 하게 된다.
따라서 목록의 각 할일에는 id가 할당이 되고 구성 요소는 페이로드로 전달이 된다. 그렇게 되면 id를 사용해서 업데이트 해야 하는 배열의 할일 또한 결정이 되고 id를 사용하여 배열에서 할 일의 인덱스를 찾을 것이므로 id가 1이면 인데긋 0을 반환하게 된다.

디스 패치 전달

확인란을 클릭하면 toggleComplete 작업을 트리거 하게 하려면 TodoItem 항목을 수정하고 작업을 해야 합니다.

import React from 'react';
import { useDispatch } from 'react-redux';
import { toggleComplete } from '../redux/todoSlice';

const TodoItem = ({ id, title, completed }) => {
	const dispatch = useDispatch();

	const handleCheckboxClick = () => {
		dispatch(toggleComplete({ id, completed: !completed }));
	};

	return (
		<li className={`list-group-item ${completed && 'list-group-item-success'}`}>
			<div className='d-flex justify-content-between'>
				<span className='d-flex align-items-center'>
					<input
						type='checkbox'
						className='mr-3'
						onClick={handleCheckboxClick}
						checked={completed}
					></input>
					{title}
				</span>
				<button className='btn btn-danger'>Delete</button>
			</div>
		</li>
	);
};

export default TodoItem;

이제 확인란을 클릭하면 toggleComplete 작업을 트리거 하려고 합니다.

TodoItem 컴포넌트에서 액션을 전달하는 클릭 핸들 함수를 생성하고 먼저 toggleComplte/useDispatch를 가져 옵니다. 다음으로 handleCheckboxClick 라는 클릭 핸들러 함수를 생성한 다음에 우리의 액션을 전달하는 화살표 함수를 생성합니다.

디스패치 함수를 호출하고 디스패치 하려는 작업을 전달을 해주고 리듀서는 변경하려는 할 일의 항목의 ID와 완료된 값이 무엇인지 알아야 하므로 이것을 페이로드 객체로 전달 해줍니다.

할일을 삭제하는 방법

할일을 삭제하면서 앱에서 이 패턴을 사용하는 또 다른 예를 만들어보자

리듀서 만들기

TodoSlice.js 로 이동하여 수정작업을 합니다.

import { createSlice } from '@reduxjs/toolkit';

export const todoSlice = createSlice({
	name: 'todos',
	initialState: [
		{ id: 1, title: 'todo1', completed: false },
		{ id: 2, title: 'todo2', completed: false },
		{ id: 3, title: 'todo3', completed: true },
		{ id: 4, title: 'todo4', completed: false },
		{ id: 5, title: 'todo5', completed: false },
	],
	reducers: {
		addTodo: (state, action) => {
			const todo = {
				id: new Date(),
				title: action.payload.title,
				completed: false,
			};
			state.push(todo);
		},
		toggleComplete: (state, action) => {
			const index = state.findIndex((todo) => todo.id === action.payload.id);
			state[index].completed = action.payload.completed;
		},
		deleteTodo: (state, action) => {
			return state.filter((todo) => todo.id !== action.payload.id);
		},
	},
});

export const { addTodo, toggleComplete, deleteTodo } = todoSlice.actions;

export default todoSlice.reducer;

감속기를 만들고 나서 작업이 전달되면 클릭된 할일의 ID를 보낸 다음 상태의 현재 목록에서 이 TODO를 필털이 합니다.

필터 기능을 사용하여 페이로드의 ID와 동일하지 않은 모든 할일을 가져옵니다. 필터함수가 새 배열을 다시 제공하므로 이것을 반환해야 합니다.

디스패치 전달

이제 삭제 버튼을 클릭할 때 delteTodo 작업을 전달해야 합니다. TodoItem.js 로 이동해서 다음으로 업데이트 합니다.

import React from "react";
import { useDispatch } from "react-redux";
import { toggleComplete, deleteTodo } from "../redux/todoSlice";

const TodoItem = ({ id, title, completed }) => {
  const dispatch = useDispatch();

  const handleCheckboxClick = () => {
    dispatch(toggleComplete({ id, completed: !completed }));
  };

  const handleDeleteClick = () => {
    dispatch(deleteTodo({ id }));
  };

  return (
    <li className={`list-group-item ${completed && "list-group-item-success"}`}>
      <div className="d-flex justify-content-between">
        <span className="d-flex align-items-center">
          <input
            type="checkbox"
            className="mr-3"
            onClick={handleCheckboxClick}
            checked={completed}
          ></input>
          {title}
        </span>
        <button onClick={handleDeleteClick} className="btn btn-danger">
          Delete
        </button>
      </div>
    </li>
  );
};

export default TodoItem;

삭제 작업을 전달을 하고 페이로드의 개체로 id를 전달하는 handleDeleteClick 함수를 생성 후 실행

완료된 총 항목을 표시하는 방법

다음으로 완료된 항목의 수를 표시하는 방법은 totalcompleteitem.js 파일에서

import React from 'react';
import { useSelector } from 'react-redux';

const TotalCompleteItems = () => {
	const todos = useSelector((state) =>
		state.todos.filter((todo) => todo.completed === true)
	);

	return <h4 className='mt-3'>Total complete items: {todos.length}</h4>;
};

export default TotalCompleteItems;

먼저 useSelector 후크를 가져오고 (라인2) 사용할 수 있도록 변수에 할당합니다.
그 이후 redux에 반환하려는 내용을 알려주는 함수를 전달하고 필터를 사용하여 완료된 값이 true 인 모든 할 일을 반환을 하고 전달되는 상태 값은 TOTAL 상태 트리이므로 여기에서 할일을 지정을 해줍니다.

이제 함수의 결과가 todos 변수에 전달되고 필터기능을 사용했기 때문에 결과는 배열로 반환됩니다.

이제 이 변수를 원하는 대로 사용할 수 있으니 결과값으로 출력을 해줍니다.

오늘은 이만 todo로 마무리하고 다음은 redux에 비동기 작업을 진행 해보자!!

profile
IT 삶을 사는 쿠키

0개의 댓글