[React js] redux, react-redux

seokki kwon·2022년 9월 24일
0

패스트캠퍼스 리덕스 기본강좌 수강완료

Redux란 왜 사용을 할까?

React 로 웹을 만들면서 props를 사용하면 매우 편리하고 직관적이나 컴포넌트가 많아지고
props를 전달하는 레벨이 깊어질수록 관리가 힘들어지고 한개의 컴포넌트에 props를 전달하기 위해
불필요하게 여러가지 컴포넌트를 거쳐야 한다( 해당 props가 필요없는)
이러한 현상을 props drilling 이라고 한다.

Redux 에 대한 오해

그렇다면 redux는 무조건 좋은것이 아닐까? 라고 생각할 수 있고
나또한 그렇게 생각했었지만 익숙하지 않은 입장에서 Redux를 사용하기 위해
준비하거나 헷갈리는 점이 많았다.(ex. 모듈화, 폴더셋팅, 라이브러리설치, 등등)

현재는 공부 목적이지만 Redux 를 사용할때 해당 사항을 고려해봐야 한다고 생각했다

설치

npm install redux react-redux redux-devtools-extension

redux는 react 전용 라이브러리가 아니다 하지만 react 와 잘맞는다고 한다.

TodoList 혼자 만들어보기

리덕스 강좌를 보면 초등학생도 따라치면 혼자할 수 있다
앞으로 리덕스 미들웨어 등등 스터디를 하기위해 혼자 안보고 만들어보기로 하였다

설치

create react-app .

.을 입력하면 해당 프로젝트 폴더이름 그대로 리액트앱이 만들어진다.

npm install redux react-redux

redux 구성에 필요한 라이브러리 설치

디자인패턴

여러가지 디자인 패턴을 써본적은 없지만 ducsk 패턴으로 디렉토리 관리를한다.

📦src
┣ 📂components
┃ ┗ 📜Todos.js
┣ 📂containers
┃ ┗ 📜TodosContainer.js
┣ 📂modules
┃ ┣ 📜index.js
┃ ┗ 📜todos.js
┣ 📜App.css
┣ 📜App.js
┣ 📜App.test.js
┣ 📜index.css
┣ 📜index.js
┣ 📜logo.svg
┣ 📜reportWebVitals.js
┗ 📜setupTests.js

components: 컴포넌트
containers:컨테이너 컴포넌트 라고 하며 주로 리덕스 액션관련한 처리를 이곳에서 담당하고 컴포넌트 props로 내려준다.
modules:reduer,action, actionFuntion 등이 모여있다

store 데이터 조회

// modules/todos.js
const ADD_TODO = "todos/ADD_TODO";
const REMOVE_TODO = "todos/REMOVE_TODO";

export const addTodo = (text) => ({ type: ADD_TODO, text });
export const removeTodo = (id) => ({ type: REMOVE_TODO, id });

const initialState = [
  {
    id: 1,
    text: "예시",
  },
];
let nextId = 1;
export default function todos(state = initialState, action) {
  switch (action.type) {
    case ADD_TODO:
      return state.concat({
        id: nextId + 1,
        text: action.text,
      });
    case REMOVE_TODO:
      return state.filter((todo) => todo.id === action.id);
    default:
      return state;
  }
}

모듈 작성을 해준다 ducsk 패턴은 action, action함수, reduecer 를 한곳에 작성한다

// modules/index.js
import { combineReducers } from "redux";
import todos from "./todos";

const rootReducer = combineReducers({
  todos,
});

export default rootReducer;

리듀서를 합쳐준다 지금은 한개뿐이라 의미가없지만 추후에 리듀서가 추가된다면 손쉽게 리듀서를 합칠 수 있다.

// index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import rootReducer from "./modules";
import { createStore } from "redux";
import { Provider } from "react-redux";
const root = ReactDOM.createRoot(document.getElementById("root"));
const store = createStore(rootReducer);
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

// 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();

store를 만들어주고 App컴포넌트에 Provieder를 이용하여 store 설정을 해준다
성공적으로 state에 데이터 조회가 된다 react앱과 store의 연결은 끝났다.

렌더링

이제 ui를 렌더링하고 적절한 이벤트를 걸어서 디스패치 함수가 호출되도록 변경해야 한다
꼭 그래야만 하는것은 아니지만 presentainonal, container 컴포넌트를 분리함으로써 관심사의
분리를 하는것을 추천한다고 한다 초보인만큼 추천하는대로 진행해보자

Presentaional

const TodoItem = () => { // 아이템
  return <div></div>;
};

const TodoList = () => { // 다수의 아이템
  return <div></div>;
};

해당 컴포넌트들은 단순히 프롭스만 받아서 렌더링을 해주는 역할만 할것이다

container

import { useSelector, useDispatch } from "react-redux";
import { addTodo, removeTodo } from "../modules/todos";
export const TodosContainer = () => {
  const todos = useSelector((state) => state.todos);
  const dispatch = useDispatch();
};

해당 컴포넌트 에서는 Presentaion 컴포넌트를 렌더하고 값을 넘겨줄것이다
리덕스와의 통신은 이곳에서 이루어진다

완성

import { useSelector, useDispatch } from "react-redux";
import { addTodo, removeTodo } from "../modules/todos";
import { TodoList } from "../components/Todos";
export const TodosContainer = () => {
  const todos = useSelector((state) => state.todos);
  const dispatch = useDispatch();

  const onCreateHandler = (text) => {
    dispatch(addTodo(text));
  };

  const onDeleteHandler = (id) => {
    dispatch(removeTodo(id));
  };

  return (
    <TodoList
      todos={todos}
      onCreate={onCreateHandler}
      onDelete={onDeleteHandler}
    />
  );
};

추가와 삭제 기능만 구현하였다

// modules/todos.js
const ADD_TODO = "todos/ADD_TODO";
const REMOVE_TODO = "todos/REMOVE_TODO";

export const addTodo = (text) => ({ type: ADD_TODO, text });
export const removeTodo = (id) => ({ type: REMOVE_TODO, id });

const initialState = [];

export default function todos(state = initialState, action) {
  switch (action.type) {
    case ADD_TODO:
      return state.concat({
        id: state.length + 1,
        text: action.text,
      });
    case REMOVE_TODO:
      return state.filter((todo) => todo.id !== action.id);
    default:
      return state;
  }
}

id의 경우 해당 리스트배열의 길이 + 1을 해주었다

// components/Todos.js
import { useState } from "react";

const TodoItem = ({ todo, onDelete }) => {
  return (
    <div>
      {todo.text}
      <span
        onClick={() => {
          onDelete(todo.id);
        }}
      >
        삭제
      </span>
    </div>
  );
};

export const TodoList = ({ todos, onCreate, onDelete }) => {
  const [text, setText] = useState("");

  return (
    <div>
      <input
        type="text"
        onChange={(e) => {
          setText(e.target.value);
        }}
      />
      <button
        onClick={() => {
          onCreate(text);
        }}
      >
        등록
      </button>
      <hr />
      <ul>
        {todos.map((todo) => (
          <TodoItem key={todo.id} todo={todo} onDelete={onDelete} />
        ))}
      </ul>
    </div>
  );
};

실제로 화면에 보여지는 부분들이다
그저 받아온 props 와 액션생성함수 들을 적절한 이벤트에 실행되도록 해주었다

profile
웹 & 앱개발 기록

0개의 댓글