본문 바로가기
내일배움캠프/Today I Learned

[TIL 2023.06.22] Redux에 대하여 2 (Action Creator, Payload, useDispatch, Ducks 패턴)

by 괴코딩 2023. 6. 23.

💻오늘 배운 내용

Action Creator

'todos' 라는 모듈이 하나 있다고 치자.

액션객체의 value를 변경할 일이 생긴다면? 만약에 프로젝트 규모가 굉장히 커서 수정할 곳이 100군데라면?

// Action value
const ADD_TODO = 'todos/ADD_TODO';

// Action Creator
export const addTodo = (payload) => {
  return {
    type: ADD_TODO,
    payload,
  };
};

const initialState = {
  todos: [],
};

const todos = (state = initialState, action) => {
  switch (action.type) {
    case ADD_TODO:
      return {
        todos: [...state.todos, action.payload],
      };
    default:
      return state;
  }
};

export default todos;

액션객체를 한 곳에서 관리할 수 있도록 “함수"와 액션 value를 상수로 만들어 관리한다.

이것을 Action Creator. 해석 그대로 액션을 만드는 생성자라고 한다.

 

 

작성법은 Ducks 패턴을 따른다.

 

Ducks 패턴

리덕스 모듈을 개발하는 개발자마다 구성요소들을 제 각각 구현하면 협업 시 여러가지 어려움이 발생한다.

Erik Rasmussn 라는 개발자가 이것을 패턴화하여 작성하는 것을 제안했는데, 그것이 바로 Ducks패턴

 

  1. Reducer 함수를 export default 한다.
  2. Action creator 함수들을 export 한다.
  3. Action type은 app/reducer/ACTION_TYPE 형태로 작성한다.

모듈 파일 1개에 Action Type, Action Creator, Reducer 가 모두 존재하는 작성방식.

 

Payload

사용자가 직접 정할 수 있게 기능을 만드려면 "~을 해" 라고 N을 같이 리듀서에서 보내야 한다.

액션객체의 type만 지정해줬을 때는 '~을' 이라는 목적어가 없었다면,

이제는 그 목적어가 생긴것이고 목적어도 액션객체에 담아 같이 보내줘야 한다는 것.

이렇게 액션객체에 같이 담아 보내주는 것을 payload라고 한다.

만약 "10을 더해" 라는 것을 리듀서에게 보내고 싶으면 액션객체에 10이라는 값의 payload를 같이 담아주는 것이다.

 

useDispatch

액션 객체를 리듀서로 보내주는 역할을 하는 hook

dispatch는 "함수"이다. 따라서  dispatch를 사용할 때 () 를 붙여서 함수를 실행한다.

예시)

import { useDispatch } from "react-redux"; // import 해온다.

// ...

const App = () => {
  const dispatch = useDispatch(); // dispatch 생성
  return (
    <div>
      <button
	// 이벤트 핸들러 추가
        onClick={() => {
	// 마우스를 클릭했을 때 dispatch가 실행되고, ()안에 있는 액션객체가 리듀서로 전달된다.
          dispatch({ type: "PLUS_ONE" }); 
        }}
      >
	+ 1
      </button>
    </div>
    );
};

export default App;

 

❓발생한 이슈/고민

이전에 만들었던 Todo List에 설정했던 state를 develop해서 Redux를 이용한 전역 데이터로 관리해보기

💡해결과정

가장 부모 컴포넌트에 있던 'todos' 라는 state를 리덕스로 옮기고 이전에 props로 전달하던 것을 다 삭제했다.

// redux/modules/todos.js

// Action value
const ADD_TODO = 'todos/ADD_TODO';
const DELETE_TODO = 'todos/DELETE_TODO';
const UPDATE_TODO = 'todos/UPDATE_TODO';

// Action Creator
export const addTodo = (payload) => {
  return {
    type: ADD_TODO,
    payload,
  };
};

export const deleteTodo = (payload) => {
  return {
    type: DELETE_TODO,
    payload,
  };
};

export const updateTodo = (payload) => {
  return {
    type: UPDATE_TODO,
    payload,
  };
};

const initialState = {
  todos: [],
};

const todos = (state = initialState, action) => {
  switch (action.type) {
    case ADD_TODO:
      return {
        todos: [...state.todos, action.payload],
      };
    case DELETE_TODO:
      return {
        todos: [...state.todos.filter((e) => e.id !== action.payload)],
      };
    case UPDATE_TODO:
      return {
        todos: [
          ...state.todos.filter((e) => {
            if (e.id === action.payload) {
              e.isDone = !e.isDone;
            }
            return e;
          }),
        ],
      };
    default:
      return state;
  }
};

export default todos;

리듀스 모듈 파일 안에 todos 추가, 삭제, 업데이트 모두 가능 하게 수정

// redux/config/configStore.js

import { createStore, combineReducers } from 'redux';
import todos from 'redux/modules/todos';
import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';

const rootReducer = combineReducers({
  todos,
});

const persistConfig = {
  key: 'root',
  storage,
};

const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer);

export default store;

redux persist를 설치하여 localStorage 사용. 새로고침 후에도 데이터가 남아있도록 하였다.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import './style/reset.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import store from 'redux/config/configStore';
import { persistStore } from 'redux-persist';

const persistor = persistStore(store);

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <App />
    </PersistGate>
  </Provider>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

redux presist 사용하려면 index.js 파일도 조금 추가사항이 생긴다.

 

대신 컴포넌트 내의 함수들은 디스패치를 사용하니까 엄청 간결해짐

// Form.jsx
  const dispatch = useDispatch();

  const onSubmitAddHandler = (e) => {
    e.preventDefault();
    if (todo.title === '' || todo.contents === '') return;

    dispatch(addTodo(todo));
    setTodo(INIT_VALUE);
  };
  
// TodoCard.jsx
  const dispatch = useDispatch();
  const todos = useSelector((state) => state.todos.todos);

  const onClickDeleteBtn = (id) => {
    const confirmDelete = window.confirm('정말 삭제하시겠습니까?');
    if (confirmDelete) {
      dispatch(deleteTodo(id));
    }
  };

  const onClickUpdateBtn = (id) => {
    dispatch(updateTodo(id));
  };

모든 컴포넌트 통틀어 이게 다임...대박 리덕스 짱짱

배울땐 욕했지만.... 무튼 짱

 

🧐궁금점과 부족한 내용

사실 최적화 같은 좀 더 개발자스러운 작업을 해보고 싶었지만...

리덕스를 써본 것에 의의를... 솔직히 어떻게 손대야 할 지 감도 안 잡힌다.

반응형