(React) 리액트 기초 16. Redux Toolkit

2023. 7. 13. 22:15
  • yarn add react-redux @reduxjs/toolkit.
├─ src
│  ├─ App.css
│  ├─ App.js
│  ├─ Components
│  │  ├─ todos
│  │  │  ├─ Form.jsx
│  │  │  └─ List.jsx
│  │  └─ ui
│  │     └─ Header.jsx
│  ├─ index.css
│  ├─ index.js
│  ├─ logo.svg
│  ├─ Pages
│  │  ├─ Detail.jsx
│  │  └─ Home.jsx
│  ├─ redux
│  │  ├─ config
│  │  │  └─ configureStore.js
│  │  └─ modules
│  │     └─ todoSlice.js
│  ├─ reportWebVitals.js
│  └─ setupTests.js

(React) 리액트 기초 15. Redux 구조 이해 (feat. TodoList) ⬅ 기본 redux로 작성했던 코드를 redux toolkit으로 바꿔보고,
다른 점은 무엇인지 알아보자. 

간단히 요약하자면 구분이 쉽게 파일명도 바꿔준 (todos→) todoSlice와 (config→) configStore, 컴포넌트에서 dispatch를 사용하는 방식을 수정해주면 되는데 코드가 훨씬 간략해지지만, 헷갈리기도 헷갈리니 잘 보자.. 

① configureStore : 

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

    const store = configureStore({
        reducer: {
            todoReducer,
           // 리듀서가 들어가는 자리. todoReducer : todoReducer; 
        },
    });

    export default store;

기존 방식의 redux보다 짧아졌다. 지금은 구조 하나하나를 깊게 이해할 필요 없으니 넘기고, todoReducer에 대해 알아보자.

② todoSlice : 

const initialState = {
    list: [
        {
            id: uuid(),
            title: '완료 버튼을 눌러보세요.',
            body: '삭제도 할 수 있어요.',
            isDone: false,
        },
        {
            id: uuid(),
            title: '취소 버튼을 눌러보세요.',
            body: '완료와 취소를 자유롭게!',
            isDone: true,
        },
    ],
};

다 지워도 initialState는 남겨놓자. 초기 객체 안에 list라는 이름의 배열을 만들고 id, title, body, isDone 속성을 넣었다.

    const todoSlice = createSlice({
        name: 'TODOS',
        initialState,
        reducers: {
            addTodo: (state, action) => {
                return {
                    ...state,
                    list: [...state.list, action.payload],
                };
            },
            deleteTodo: (state, action) => {
                state.list = state.list.filter(todo => todo.id !== action.payload);
            },
            switchTodo: (state, action) => {
                state.list = state.list.map(todo => {
                    if (todo.id === action.payload) {
                        return { ...todo, isDone: !todo.isDone };
                    } else {
                        return todo;
                    }
                });
            },
        },
    });

    export const { addTodo, deleteTodo, switchTodo } = todoSlice.actions;
    export default todoSlice.reducer;

modules를 작성할 때 불필요하게 반복되는 것 같던 Action Value와 Action Creator를 없애고 Reducer 하나로 합쳤다. Reducer도 더이상 switch문을 쓸 필요가 없다. 

const nameSlice = createSlice({
        name: 'NAME속성',
        initialState,
        reducers: { TYPE : (action,type) => {} } 
})

export const { TYPE } = nameSilce.actions;
export default nameSlice.reduer;

initialState에서 배열이름을 state뒤에 붙여주는걸 잊지 말자!
코드의 일관성과 가독성을 유지하고 다른 개발자와의 협업을 쉽게 해준다. 

③ Form , Input 컴포넌트  : 

const addBtnHandler = event => {
        // input창이 비어있는지 아닌지를 검증하는 절차 (문제점 : 리랜더링 되면서 데이터 날라감)
        if (!title || !body) {
            alert('필수값이 누락되었습니다.');
            return false;
        }
        event.preventDefault();

        const newTodo = {
            id: uuid(),
            title,
            body,
            isDone: false,
        };

        dispatch(addTodo(newTodo));

        setTitle('');
        setBody('');
    };

addTodo에서 계속 애를 먹었다. 기존의 redux는 Action 객체를 직접 생성해서 dispatch 해왔는데, 이제 reducer 함수로 다 합쳐졌기 때문에 따로 써줄 필요가 없어서 간편하다. 

기존 reducer 에서의 dispatch : 

dispatch({
        type: 'ADD_TODO',
        payload: {
            id: uuid(),
            title,
            body,
            isDone: false,
        },
    });

Redcer-Toolkit에서의 dispatch :

(1) useDispatch 훅을 사용하여 디스패치 함수를 import한다. 
import { useDispatch } from 'react-redux';
const dispatch = useDispatch();

(2) dispatch 함수로 action 객체를 dispatch 한다.
import { addTodo } from '../redux/modules/todoSlice';
dispatch( action 함수 ( { payload : {newObj} } ) )
action 함수 (addTodo)를 바로 호출할 수 있는게 장점이다. import도 잊지 말기.  

(3) newObj는 payload 속성에 전달할 내용이다. 이 경우에는 id, title, body, isDone을 말한다. 

                <button onClick={() => dispatch(switchTodo(todo.id))}>취소</button>
                <button onClick={() => dispatch(deleteTodo(todo.id))}>삭제</button>

여기서 action 함수 뒤의 (todo.id)는 아이템의 고유한 식별자 값으로, todoReducer.map( todo => ~ ) 매서드에서 item명을 todo로 설정해서 그렇다.