[React] 리액트 없이 쓰는 리덕스_②

겨레·2024년 12월 12일

[React] 리액트 스터디

목록 보기
91/95

UI 구성하기

① 스타일 작성

  • index.css
.toggle {
    border: 2px solid black;
    width: 64px;
    height: 64px;
    border-radius: 32px;
    box-sizing: border-box;
}

.toggle.active {
    background: yellow;
  }

② index.html 수정

  • index.html
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="index.css">
  </head>
  <body>
    <div class="toggle"></div>
    <hr/>
    <h1>0</h1>
    <button id="increase">+1</button>
    <button id="decrease">-1</button>
    <script src="./index.jsx"></script>
  </body>
</html>


DOM 레퍼런스 만들기

UI를 관리할 때 별도의 라이브러리를 사용하지 않음!
따라서 DOM을 직접 수정해 줘야 한다.

다음과 같이 자바스크립트 파일 상단에 수정할 DOM 노드를 가리키는 값을 미리 선언해 주고, 기존 코드는 지운다.


액션 타입과 액션 생성 함수 정의

프로젝트의 상태에 변화를 일으키는 것이 바로 액션!

  • 먼저 액션 이름을 정의한다.
    ✔ 액션 이름은 문자열 형태로, 주로 대문자로 작성함.
    ✔ 액션 이름은 고유해야 함.

    • index.jsx

       // 액션 이름 정의
      const TOGGLE_SWITCH = 'TOGGLE_SWITCH';
      const INCREASE = 'INCREASE';
      const DECREASE = 'DECREASE';
      

  • 액션 이름을 사용해 액션 객체를 만드는 액션 생성 함수를 작성
    ✔ 액션 객체는 type 값을 반드시 갖고 있어야 함.
    ✔ 그 외에 추후 상태를 업데이트할 때 참고하고 싶은 값은 마음대로 넣을 수 있음.

    • index.jsx 이어서 추가
      // 액션 생성 함수
      const toggleSwitch = () => ({ type: TOGGLE_SWITCH });
      const increase = (difference) => ({ type: INCREASE, difference });
      const decrease = () => ({ type: DECREASE });

초깃값 설정

초깃값의 형태는 자유!
✔ 숫자일 수도 있고, 문자열일 수도 있고, 객체일 수도 있음.

  • index.jsx 이어서 추가
    // 초깃값 설정
    const initialState = {
    toggle: false,
    counter: 0,
    };

리듀서 함수 정의

리듀서는 변화를 일으키는 함수이다.
✔ 함수의 파라미터로는 state와 action 값을 받아 옴.

  • index.jsx 이어서 추가
    /* 리듀서 함수 정의 */ 
    // 리듀서 함수 맨 처음 호출 시 state가 undefined임.
    // undefined일 때 => initialState를 기본값으로 사용
    function reducer(state = initialState, action) {
    // action.type에 따라 다른 작업을 처리
    switch (action.type) {
      case TOGGLE_SWITCH:
        return {
          ...state, // 상태의 불변성은 유지하되 데이터에 변화를 일으킴
          toggle: !state.toggle,
        };
      case INCREASE:
        return {
          ...state,
          counter: state.counter + action.difference,
        };
      case DECREASE:
        return {
          ...state,
          counter: state.counter - 1,
        };
      default:
        return state;
    }
    }

스토어 만들기

스토어를 만들 때는 createStore 함수를 사용함!
✔ 상단에 import 해서 리덕스에서 해당 함수를 불러옴.
함수의 파라미터에는 리듀서 함수를 넣어줘야 함.


  • index.jsx 상단에 추가
import { configureStore } from '@reduxjs/toolkit';

/* 6. 스토어 만들기 */
const store = configureStore({ reducer }); // 상단에 import 필수

(+) reateStore → configureStore 권장


스토어 내장 함수 사용하기

스토어를 생성했으니, 스토어 내장 함수들을 사용해 줄 차례입니다.

① render 함수 만들기

  • render 함수
    상태가 업데이트될 때마다 호출
    ✔ 리액트의 render 함수와 달리 이미 html을 사용해 만들어진 UI 속성을 상태에 따라 변경

  • index.jsx 상단에 추가

/* 7. store 내장 함수 사용 */
// 7-1) render 함수
const render = () => {
  const state = store.getState(); // 현재 상태 불러옴
  // 토글 처리
  if (state.toggle) {
    divToggle.classList.add("active");
  } else {
    divToggle.classList.remove("active");
  }
  // 카운터 처리
  counter.innerText = state.counter;
};
reducer();

② 구독하기
스토어의 상태가 바뀔 때마다 render 함수가 호출되도록 해보자!

토어의 내장 함수 subscribe를 사용해 수행할 수 있음.
✔ subscribe 함수의 파라미터로는 함수 형태의 값을 전달해 줌.
✔ 이렇게 전달된 함수는 추후 액션이 발생하여 상태가 업데이트될 때마다 호출됨.

  • index.jsx 상단에 추가
// 7-2) subscribe 함수
const listener = () => {
    console.log('상태가 업데이트됨');  
  }
  const unsubscribe = store.subscribe(listener);
  
  unsubscribe(); // 추후 구독을 비활성화할 때 함수를 호출

(+) subscribe 함수를 직접 사용함. 추후 리액트 프로젝트에서 리덕스를 사용할 때는 이 함수를 직접 사용하지 않을 것!!

왜냐하면, 컴포넌트에서 리덕스 상태를 조회하는 과정에서 react-redux라는 라이브러리가 이 작업을 대신해 주기 때문~


상태가 업데이트될 때마다 render 함수를 호출

/ 8.상태 업데이트 시, render 함수 호출 /

  • 지금까지 작성된 idex.jsx 전체 코드
import { configureStore } from '@reduxjs/toolkit';

/* 6. 스토어 만들기 */
const store = configureStore({ reducer }); // 상단에 import 필수

/* 7. store 내장 함수 사용 */
// 7-1) render 함수
const render = () => {
  const state = store.getState(); // 현재 상태 불러옴
  // 토글 처리
  if (state.toggle) {
    divToggle.classList.add("active");
  } else {
    divToggle.classList.remove("active");
  }
  // 카운터 처리
  counter.innerText = state.counter;
};
reducer();
store.subscribe(render); /* 8.상태 업데이트 시, render 함수 호출 */

(+) reducer();는 직접 호출하지 않아도 된다.
Redux 스토어가 내부적으로 리듀서를 호출하기 때문!


액션 발생시키기

액션을 발생시키는 것을 디스패치(dispatch)라고 함!

✔ 디스패치를 할 때는 스토어의 내장 함수 dispatch를 사용하고
파라미터는 액션 객체를 넣어 주면 됨.

각 DOM 요소에 클릭 이벤트를 설정!
이벤트 함수 내부에서는 dispatch 함수를 사용해 액션을 스토어에게 전달해 준다.


  • idex.jsx 전체 코드
import { configureStore } from "@reduxjs/toolkit";

/* 1. DOM 레퍼런스 만들기 */
const divToggle = document.querySelector(".toggle");
const counter = document.querySelector("h1");
const btnIncrease = document.querySelector("#increase");
const btnDecrease = document.querySelector("#decrease");

/* 2. 액션 이름 정의 */
const TOGGLE_SWITCH = "TOGGLE_SWITCH";
const INCREASE = "INCREASE";
const DECREASE = "DECREASE";

/* 3. 액션 생성 함수 */
const toggleSwitch = () => ({ type: TOGGLE_SWITCH });
const increase = (difference) => ({ type: INCREASE, difference });
const decrease = () => ({ type: DECREASE });

/* 4. 초깃값 설정 */
const initialState = {
  toggle: false,
  counter: 0,
};

/* 5. 리듀서 함수 정의 */
// 리듀서 함수 맨 처음 호출 시 state가 undefined임.
// undefined일 때 => initialState를 기본값으로 사용
function reducer(state = initialState, action) {
  switch (action.type) {
    case TOGGLE_SWITCH:
      return {
        ...state, // 상태의 불변성은 유지하되 데이터에 변화를 일으킴
        toggle: !state.toggle,
      };
    case INCREASE:
      return {
        ...state,
        counter: state.counter + action.difference,
      };
    case DECREASE:
      return {
        ...state,
        counter: state.counter - 1,
      };
    default:
      return state;
  }
}


/* 6. 스토어 만들기 */
const store = configureStore({ reducer });


/* 7. store 내장 함수 사용 */
// 7-1) render 함수
const render = () => {
  const state = store.getState(); // 현재 상태 불러옴
  // 토글 처리
  if (state.toggle) {
    divToggle.classList.add("active");
  } else {
    divToggle.classList.remove("active");
  }
  // 카운터 처리
  counter.innerText = state.counter;
};

// (+) reducer();는 직접 호출하지 않아도 됨!
// => Redux 스토어가 내부적으로 리듀서를 호출하기 때문
// reducer();
store.subscribe(render); /* 8.상태 업데이트 시, render 함수 호출 */


/* 9. 액션 발생시키기 */
divToggle.onclick = () => {
  store.dispatch(toggleSwitch());
};
btnIncrease.onclick = () => {
  store.dispatch(increase(1));
};
btnDecrease.onclick = () => {
  store.dispatch(decrease());
};

// 7-2) subscribe 함수
const listener = () => {
  console.log("상태가 업데이트됨");
};
const unsubscribe = store.subscribe(listener);

// 추후 구독을 비활성화할 때 함수를 호출
unsubscribe();

/* 10. DOMContentLoaded 이벤트 리스너 */
document.addEventListener("DOMContentLoaded", () => {
  // 초기 렌더링을 트리거하기 위한 호출
  render();
});

profile
호떡 신문지에서 개발자로 환생

0개의 댓글