[TIL] Redux Basics

Alex J. Lee·2021년 11월 2일
0

TIL

목록 보기
44/58

Today I Learned

Redux란?

  • Redux는 자바스크립트 앱을 위한 예측가능한 상태 (state) 컨테이너이다.

Redux를 사용하는 이유

  • React에서 상태는 각 컴포넌트 안에서 관리되며, 자식 컴포넌트들끼리 데이터 공유를 하려면 공통 부모 컴포넌트에 상태를 두고 주고받아야 한다. 뎁스가 얕을 때는 괜찮지만 깊어지면 상태 관리가 어려워진다. 이를 해결하기 위해 상태 관리 라이브러리인 Redux를 사용할 수 있다.

Redux의 세 가지 원칙

  1. Single source of truth
    • 데이터 저장 공간 (store) 는 하나 뿐이다.
  2. State is read-only
    • state를 바꾸기 위해서는 반드시 setter사용해야 한다. (Redux에서는 action 객체를 통해 state를 변경할 수 있다)
  3. Changes are made with pure function
    • reducer는 side-effect가 없는 순수함수이다.

Redux Store

  • 상태가 관리되는 단 하나뿐인 공간. 어플리케이션 당 하나의 store만 존재한다.
  • 앱에 필요한 state를 store에 저장해두고 여기에 접근하여 사용한다.
// src/store/store.js
import { createStore } from 'redux';
import reducer from '../reducers/index'

const store = createStore(reducer);
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import store from './store/store';
import { Provider } from 'react-redux';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

Redux Action

  • 자바스크립트 객체. 반드시 type (action type) 속성을 가져야 하며 그외 payload는 필요에 따라 작성한다. type은 문자열이어야 하지만 payload는 어떤 데이터 타입도 가능하다.
  • store에 앱 데이터(type을 비롯한 다양한 데이터)를 운반하는 역할을 한다.
// src/actions/index.js

// action type
export const ADD_ITEM = "ADD_ITEM";
// actions creator functions
export const addItem = (itemId) => {
  return {
    type: 'ADD_ITEM',
    payload: {
      itemId,
      quantity: 1
    }
  }
}

Redux Reducer

  • 현재 state와 action을 이용해 다음 상태를 만들어낸다.
  • reducer는 side effect가 없는 순수함수이다.
  • dispatch는 action 객체를 인자로 받고, reducer를 호출하여 새로운 state를 생성한다.
    • dispatch(action(args))
  • 데이터가 한 방향으로만 흘러야 하기 때문에 reducer를 사용한다.
// src/reducers/index.js

import { ADD_ITEM } from "../actions/index";
import { initialState } from "./initialState";

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_ITEM:
      return Object.assign({}, state, {
        cartItems: [...state.cartItems, action.payload]
      });
    	break
    default:
      return state;
  }
}
export default reducer;

Redux 장점

  1. 상태를 예측 가능하게 만들어준다. (reducer는 예측가능한 순수함수)
  2. 유지보수가 용이하다. (props를 사용할 때보다 수정이 용이)
  3. 디버깅에 유리하다. (action과 state log 기록 시)
  4. 순수함수를 사용하기 때문에 테스트가 용이하다.

Getting Started - Step by Step

Redux Tutorial - Quick Start

  1. Installation
  • Redux Toolkit 패키지와 React-Redux 패키지를 프로젝트에 설치하기
npm install @reduxjs/toolkit react-redux
  • React Redux로 새로운 앱 만들기
npm create-react-app app-name --template redux
  1. Redux store 생성하기
  • src/store/store.js 파일을 생성한 뒤, @reduxjs/toolkitconfigureStore API를 불러와 Redux store를 생성하기
// src/store/store.js
import { configureStore } from '@reduxjs/toolkit';

export default configureStore({
  reducer: {}
});
  1. React components에서 생성한 state에 접근할 수 있도록 만들어주기
  • src/index.js에서 react-reduxProvider API를 불러와 <Provider><App> 컴포넌트 감싸주기
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import store from './app/store';
import { Provider } from 'react-redux';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root');
)
  1. Redux State Slice 생성하기
  • src/features/counter/counterSlice.js 파일을 생성한 뒤, @reduxjs/toolkitcreateSlice API를 불러와 Slice 생성하기
  • Redux Toolkit을 사용하면 createSlice를 사용하여 state, action, reducer를 한 곳에서 모두 정의할 수 있다.
// src/features/counter/counterSlice.js
import { createSlice } from '@reduxjs/toolkit';
 
// Slice : state, reducers (& actions)로 구성됨
export const counterSlice = createSlice({
  // 구분 가능한 slice 이름
  name: 'counter', 
  // state 초기값 설정
  initialState: {
    value: 0
  }, 
  // actionName: reducerFunction
  reducers: {
    increment: (state) => {
      state.count += 1;
    },
    decrement: (state) => {
      state.count -= 1;
    },
    incrementBy: (state, action) => {
      state.count += action.payload;
    }
  }
});

// export actions from the slice
export const { increment, decrement } = counterSlice.actions;
// export reducers from the slice
export default counterSlice.reducer;
  1. Store에 Slice Reducer 추가하기
  • 생성한 slice의 reducer 함수를 store에 추가하여 컴포넌트들에서 사용가능하도록 만들어주기
// src/store/store.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';

export default configureStore({
  reducer: {
    counter: counterReducer
  }
});
  1. React State대신 Redux Store를 사용하도록 코드 변경
// src/features/counter/Counter.js
// BEFORE
import React from 'react';
import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div className="counter">
      <h1>Count : {count}</h1>
      <button onClick={() => setCount(count+1)}>Increment</button>
      <button onClick={() => setCount(count-1)}>Decrement</button>
    </div>
  )
}
// src/features/counter/Counter.js
// AFTER
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { decrement, increment } from './counterSlice';

export default function Counter() {
  const { count } = useSelector(state => state.counter);
  const dispatch = useDispatch();
  return (
    <div className="counter">
      <h1>Count : {count}</h1>
      <button onClick={() => dispatch(increment())}>Increment</button>
      <button onClick={() => dispatch(decrement())}>Decrement</button>
      <button onClick={() => dispatch(incrementBy(10))}>Increment by 10</button>
    </div>
  )
}
profile
🦄✨글 잘 쓰는 개발자가 되기 위해 꾸준히 기록합니다 ✨🦄

0개의 댓글