본문 바로가기
카테고리 없음

[React] Redux combineReducers

by dev_kong 2022. 5. 3.
728x90
728x90

0. 목차

1. 개요

2. combineReducers

1. 개요

어제 포스팅했던 내용에서 이어진다.

// ./reducer/index.js

const initialState = {
  number: 0,
};

const rootReducer = (state = initialState, action) => {
  const { type } = action;
  switch (type) {
    case 'increase':
      return {
        ...state,
        number: state.number + 1,
      };

    case 'decrease':
      return {
        ...state,
        number: state.number - 1,
      };

    default:
      return state;
  }
};

export default rootReducer;

이런식으로 redux 와 reducer를 만들어서 전역에서 관리되는 상태를 만들고 변경하는 걸 했었다.

현재는 initialState 의 프로퍼티가 number하나 뿐이지만
실제로 작업할때는 정말 많은 갯수의 프로퍼티가 들어갈거다.

오늘은 comment 부분을 만들어 보려 하는데
우선은 index.js 에 다 욱여 넣어보고,
combineReducers를 이용해 reducer를 나누고 합치는걸 해보려고 한다.

2. combineReducers

우선 comment 상태를 만들고,
comment 상태를 comment 컴포넌트에 랜더 해보자.


const initialState = {
  number: 0,
  comment: [
    {
      userAlias: 'kong',
      content: 'ㅎㅇㅎㅇ',
      date: '2022-05-01',
    },
    {
      userAlias: 'pea',
      content: 'ㅎㅇㅎㅇ2',
      date: '2022-05-03',
    },
  ],
};

상태 만들고,

// ./src/pages/comment/index.jsx

import { useSelector, useDispatch } from 'react-redux';

const Comment = () => {
  const comment = useSelector((state) =>
    state.comment.map((v) => {
      return (
        <li>
          <ul>
            <li>작성자: {v.userAliase}</li>
            <li>내용: {v.content}</li>
            <li>날짜: {v.date}</li>
          </ul>
        </li>
      );
    })
  );

  return (
    <>
      <h3>Comment Component</h3>
      <ul>{comment}</ul>
    </>
  );
};

export default Comment;

useSelector로 상태를 가져오고 상태에서 comment 만 가져온뒤 map 을 이용해서 jsx 문법으로 화면울 구성하게끔 만들어 주었다.

여기다가 댓글을 작성할 수 있는 input을 만들어주자.


import { useSelector, useDispatch } from 'react-redux';
import { useState } from 'react';

const Comment = () => {
  const initialState = {
    userAlias: '',
    content: '',
  };

  const [commentState, setCommentState] = useState(initialState);

  const changeValue = (e) => {
    const {
      target: { name, value },
    } = e;

    setCommentState({
      ...commentState,
      [name]: value,
    });
  };

  const comment = useSelector((state) =>
    state.comment.map((v) => {
      return (
        <li>
          <ul>
            <li>작성자: {v.userAliase}</li>
            <li>내용: {v.content}</li>
            <li>날짜: {v.date}</li>
          </ul>
        </li>
      );
    })
  );
  return (
    <>
      <h3>Comment Component</h3>
      <form action="">
        <input
          type="text"
          name="userAlias"
          placeholder="작성자"
          onChange={changeValue}
        />
        <input
          type="text"
          name="content"
          placeholder="내용"
          onChange={changeValue}
        />
        <button type="submit">등록</button>
      </form>
      <ul>{comment}</ul>
    </>
  );
};

export default Comment;

input의 value에따라 변화하는 상태는 굳이 전역에서 관리할 필요가 없으므로,
해당 컴포넌트에 박아두면 된다.

커스텀 훅을 만들어서 따로 관리할 수 있지만,
그게 목적이 아니니까 이번에는 그냥 할거다.

이제 form 이 submit 될때 submit 을 막고,
dispatch를 이용해 전역상태를 변경해주면 된다.


import { useSelector, useDispatch } from 'react-redux';
import { useState } from 'react';

const Comment = () => {
  const dispatch = useDispatch();

  const initialState = {
    userAlias: '',
    content: '',
  };

  const [commentState, setCommentState] = useState(initialState);

  const changeValue = (e) => {
    const {
      target: { name, value },
    } = e;

    setCommentState({
      ...commentState,
      [name]: value,
    });
  };

  const comment = useSelector((state) =>
    state.comment.map((v) => {
      return (
        <li>
          <ul>
            <li>작성자: {v.userAliase}</li>
            <li>내용: {v.content}</li>
            <li>날짜: {v.date}</li>
          </ul>
        </li>
      );
    })
  );

  const submitHandler = (e) => {
    e.preventDefault();

    if (commentState.userAlias && commentState.content) {
      dispatch({ type: 'ADD_COMMENT', payload: commentState });
    }
  };
  return (
    <>
      <h3>Comment Component</h3>
      <form action="" onSubmit={submitHandler}>
        <input
          type="text"
          name="userAlias"
          placeholder="작성자"
          onChange={changeValue}
        />
        <input
          type="text"
          name="content"
          placeholder="내용"
          onChange={changeValue}
        />
        <button type="submit">등록</button>
      </form>
      <ul>{comment}</ul>
    </>
  );
};

export default Comment;

comment 컴포넌트에서는 이렇게 만들어주고


const rootReducer = (state = initialState, action) => {
  const { type, payload } = action;
  switch (type) {
    case 'increase':
      return {
        ...state,
        number: state.number + 1,
      };

    case 'decrease':
      return {
        ...state,
        number: state.number - 1,
      };

    case 'ADD_COMMENT':
      const nowDate = new Date();
      const date = `${nowDate.getFullYear()}-${
        nowDate.getMonth() + 1
      }-${nowDate.getDate()}`;
      payload.date = date;
      const newComment = [...state.comment, payload];
      return {
        ...state,
        comment: newComment,
      };

    default:
      return state;
  }
};

루트 리듀서도 수정을 해주었다.

그런데 지금 댓글 추가 기능만 만들엇는데도 이이이만큼 길어지는데,

삭제 업데이트 까지 만들면 더 길어질거다.

그래서 reducer를 다 쪼개고,
combineReducers로 합쳐주면 코드가 읽기도 편하고
관리하기도 편하고, 재사용성도 높아진다.

그렇다고 한다..

그럼 쪼개 보자.

// ./src/reducer/counter/counter.js

const initalState = {
  number: 0,
};

const counter = (state = initalState, action) => {
  const { type, payload } = action;
  switch (type) {
    case 'increase':
      return {
        ...state,
        number: state.number + 1,
      };

    case 'decrease':
      return {
        ...state,
        number: state.number - 1,
      };

    default:
      return state;
  }
};

export default counter;

카운터를 담당하는 reducer 를 만들고

// ./src/reducer/comment/commentReducer.js

const initialState = {
  commentList: [
    {
      userAlias: 'kong',
      content: 'ㅎㅇㅎㅇ',
      date: '2022-05-01',
    },
    {
      userAlias: 'pea',
      content: 'ㅎㅇㅎㅇ2',
      date: '2022-05-03',
    },
  ],
};

const comment = (state = initialState, action) => {
  const { type, payload } = action;
  switch (type) {
    case 'ADD_COMMENT':
      const nowDate = new Date();
      const date = `${nowDate.getFullYear()}-${
        nowDate.getMonth() + 1
      }-${nowDate.getDate()}`;
      payload.date = date;
      const newComment = [...state.comment, payload];
      return {
        ...state,
        comment: newComment,
      };

    default:
      return state;
  }
};

export default comment;

comment 를 담당하는 리듀서를 만들었다.

// ./src/reducer/index.js

import { combineReducers } from 'redux';

import comment from './comment/commentReducer';
import counter from './counter/counter';

const rootReducer = combineReducers({
  comment,
  counter,
});

export default rootReducer;

rootReducer 에서 combineReducers 를 이용해서
각각의 reducer를 합쳐준다.

이렇게 하면 전역에서 관리하는 상태는


state = {
    comment: [
      // ...
    ],
      counter : {
         // ...
    }
}

이런 형태가 된다.

각각 컴포넌트에서 변경된 상태에 따라 조금씩 코드를 수정해주면 정상적으로 동작하는걸 확인 할수 있다.

const initialState = {
  commentList: [
    {
      userAlias: 'kong',
      content: 'ㅎㅇㅎㅇ',
      date: '2022-05-01',
    },
    {
      userAlias: 'pea',
      content: 'ㅎㅇㅎㅇ2',
      date: '2022-05-03',
    },
  ],
};

const comment = (state = initialState, action) => {
  const { type, payload } = action;
  switch (type) {
    case 'ADD_COMMENT':
      const nowDate = new Date();
      const date = `${nowDate.getFullYear()}-${
        nowDate.getMonth() + 1
      }-${nowDate.getDate()}`;
      payload.date = date;
      const newCommnetList = [...state.commentList, payload];
      return {
        ...state,
        commentList: newCommnetList,
      };

    default:
      return state;
  }
};

export default comment;

너무 편안하다.

전체코드는 Github

728x90
728x90

댓글