👨🏻‍💻 모각코

Redux 설치 및 사용하기

kangkibong 2023. 7. 22. 19:11

Redux 설치

  • redux와 react-redux 패키지를 install한다.
  • react-redux는 react에서 redux를 편하게 사용하기 위한 context와 hook을 제공한다.
npm install redux
npm install react-redux

redux or store 폴더 생성

  • reducer, types, actions, index(reducer, actions의 export를 하나로 합치는 파일)파일을 생성한다.
  • rootReducer관련한 root위치에 index파일을 생성한다.

actions 정의

  • addTask, removeTask, updateTask과 같은 actions들을 정의한다.
export const addTask = (content: string) => {};

export const updateTask = (
  id: string,
  contnet: string,
  complete: boolean
) => {};

export const removeTask = (id: string) => {};

reducer 정의

  • 기존 상태를 의미하는 state와 변화를 위한 action을 받는 reducer를 정의한다.
// state: 기존 상태, action: 변화를 위한 action
export const tasks = (state, action) => {};

types 정의

  • reducer에 필요한 stateaction의 type을 지정한다.
  • actions의 type은 각 action의 type에 해당하는 ActionTypes으로 지정한다.
  • action의 type은 각 typepayload를 받고 type은 전에 정의해둔 ActionTypes로 지정하고 payload는 state의 type으로 지정한다.
// state에 대한 타입을 정의한다.
export interface Task {
  id: string;
  content: string;
  complete: boolean;
}

// actions에 대한 타입을 정의한다.
export type ActionTypes = "ADD_TASK" | "UPDATE_TASK" | "REMOVE_TASK";

// action에 대한 타입을 정의한다.
// action의 타입은 action들의 type과 payload(실제로 들어가는 값)을 받는다.
export type Action = {
  type: ActionTypes,
  payload: Task
}
  • type정의가 완료되면 reducer에 type을 import하여 부여한다.
import { Action, Task } from "./types";

export const tasks = (state: Task[], action: Action) => {};

reducer 로직 구현

  • action에 대한 로직을 switch문으로 구현한다.
export const tasks = (state: Task[] = [], action: Action) => { // Task[] = [] undefined 발생을 막기 위해
  switch (action.type) {
    case "ADD_TASK": {
      const newTask = action.payload;
      return [...state, newTask];
    }
    case "UPDATE_TASK": {
      const updatedTask = action.payload;
      return state.map((oldTask) =>
        oldTask.id === updatedTask.id ? updatedTask : oldTask
      );
    }
    case "REMOVE_TASK": {
      const removedTask = action.payload;
      return state.filter((task) => task.id !== removedTask.id);
    }
    default: {
      return state;
    }
  }
};

action 구현

  • 정의 해놓은 action을 type과 payload를 return하는 로직을 구현한다.
import { Action } from "./types";
import { v4 } from "uuid";

// reducer에서 사용할 수 있도록 action을 정의한다.
export const addTask = (content: string): Action => {
  return {
    type: "ADD_TASK",
    payload: {
      id: v4(),
      content,
      complete: false,
    },
  };
};

export const updateTask = (
  id: string,
  content: string,
  complete: boolean
): Action => {
  return {
    type: "UPDATE_TASK",
    payload: {
      id,
      content,
      complete,
    },
  };
};

export const removeTask = (id: string): Action => {
  return {
    type: "REMOVE_TASK",
    payload: {
      id,
      content: "",
      complete: false,
    },
  };
};

index.ts

  • 구현했던 action과 reducer를 해당 폴더의 index.ts에 모두 export한다.
export * from "./actions";
export * from "./reducer";

root reducer & create store

  • 구현 했던 모든 reducer들을 redux폴더의 최상위 root인 index.ts에 rootReducer로 combine한다.
  • combine한 reducer들을 기반으로 store를 생성하고 이를 전역 Provider로 전달하게 끔 구현한다.
  • createStore에 취소선이 생기는 이유는 redux core가 아닌 redux toolkit으로 구현하는 것을 추천하고 있기 때문이다.
import { createStore } from 'redux';
import { combineReducers } from "redux";
import { tasks } from "./tasks";

// root reducer를 만든다.
// react-redux에서 제공하는 전역 Provider를 통해 store값 들을 App컴포넌트에 전달하기 위해서 reducer들을 전부 합쳐주는 최상위 root인 index.ts에 root reducer을 구현해야한다.

// 모든 reducer들을 combineReducers에 전달하여 합친다.
const rootReducer = combineReducers({ tasks });

// 만든 rootReducer를 가지고 store를 생성하여 export할 수 있게한다.
export const store = createStore(rootReducer)

// RootState를 rootReducer의 타입으로 지정한다.
export type RootState = ReturnType<typeof rootReducer>
  • 이후 App컴포넌트를 감싸는 src폴더의 최상위 root인 index.tsx에 전역 Provider로 감싸고 store를 전달한다.
import { Provider } from "react-redux";
import { store } from "./redux";

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);

root.render(
  <Provider store={store}>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </Provider>
);

useDispatch & useSelector

useDispatch

  • react-redux에서 제공하는 useDispatch를 사용하여 reducer를 사용할 수 있다.
import { useDispatch } from "react-redux";
import { addTask, updateTask, removeTask } from "../../redux/tasks";

const dispatch = useDispatch()

dispatch(addTask(content));
dispatch(updateTask(id, content, complete))
dispatch(removeTask(id))

useSelector

  • react-redux에서 제공하는 useSelector를 사용하여 RootState인 root store의 특정 store를 가져올 수 있다.
import { useSelector } from "react-redux";
import { RootState } from "../../redux";

const tasks = useSelector((store: RootState) => store.tasks);

return (
    <UnorderedList {...props}>
      {tasks.map((item) => (
        <Task
          key={item.id}
          id={item.id}
          content={item.content}
          complete={item.complete}
        />
      ))}
    </UnorderedList>
  );