Redux #4 리덕스 기본 형태 다뤄보기

리덕스만 다루는 포스팅이다. 다음에 리액트와 리덕스를 연동해서 사용하는 방법부터 제대로 알아보자.

1. 리덕스의 기본 형태

// store를 만들어주는 함수 호출
import { createStore } from 'redux';

// redux에서 관리할 초기 상태를 정의
const initialState = {
  counter: 0,
  text: '',
  list: [],
};

// 액션 타입 상수들 정의
const INCREASE = 'INCREASE';
const DECREASE = 'DECREASE';
const CHANGE_TEXT = 'CHANGE_TEXT';
const ADD_TO_LIST = 'ADD_TO_LIST';

// 액션 생성 함수
const increase = () => ({
  type: INCREASE,
});

const decrease = () => ({
  type: DECREASE,
});

const changeText = text => ({
  type: CHANGE_TEXT,
  text,
});

const addToList = item => ({
  type: ADD_TO_LIST,
  item,
});

// reducer 함수
function reducer(state = initialState, action) {
  switch (action.type) {
    case INCREASE:
      return {
        ...state,
        counter: state.counter + 1,
      };
    case DECREASE:
      return {
        ...state,
        counter: state.counter - 1,
      };
    case CHANGE_TEXT:
      return {
        ...state,
        text: action.text,
      };
    case ADD_TO_LIST:
      return {
        ...state,
        list: state.list.concat(action.item),
      };
    default:
      return state;
  }
}

// store 생성
const store = createStore(reducer);
console.log(store.getState()); // 상태 조회

리덕스의 초기 상태는 왜 넣어줘야 할까?

함수 reducer에는 state에 초기값을 설정해줘야 한다.
리덕스가 처음에 reducer를 실행할 때 state에 undefined라면 default case가 실행되고 undefined가 그대로 return 될 것이다.
그렇기 때문에 아래와 같이 reducer에는 초기상태를 지정해줘야 한다.

const initialState = {
  counter: 0;
}

function reducer(state = initialState, action) {
  switch(action.type) {
    case INCREASE:
      return {
        ...state,
		counter: state.counter + 1
      }
    default:
      return state;
  }
}

2.구독, 디스패치

스토어에 구독을 하기 위해 리스너라는 함수를 만들어보자.

const listener = () => {
  const state = store.getState();
  console.log(state);
}

const unsubscribe = store.subscribe(listener);
// 만약 구독을 해지하고 싶을 때 해당 함수를 호출만하면된다.
unsubscribe();

결과 코드

// store를 만들어주는 함수 호출
import { createStore } from 'redux';

// redux에서 관리할 초기 상태를 정의
const initialState = {
  counter: 0,
  text: '',
  list: [],
};

// 액션 타입 상수들 정의
const INCREASE = 'INCREASE';
const DECREASE = 'DECREASE';
const CHANGE_TEXT = 'CHANGE_TEXT';
const ADD_TO_LIST = 'ADD_TO_LIST';

// 액션 생성 함수
const increase = () => ({
  type: INCREASE,
});

const decrease = () => ({
  type: DECREASE,
});

const changeText = text => ({
  type: CHANGE_TEXT,
  text,
});

const addToList = item => ({
  type: ADD_TO_LIST,
  item,
});

// reducer 함수
function reducer(state = initialState, action) {
  switch (action.type) {
    case INCREASE:
      return {
        ...state,
        counter: state.counter + 1,
      };
    case DECREASE:
      return {
        ...state,
        counter: state.counter - 1,
      };
    case CHANGE_TEXT:
      return {
        ...state,
        text: action.text,
      };
    case ADD_TO_LIST:
      return {
        ...state,
        list: state.list.concat(action.item),
      };
    default:
      return state;
  }
}

// store 생성
const store = createStore(reducer);
console.log(store.getState()); // 상태 조회

const unsubscribe = store.subscribe(() => {
  console.log(store.getState()); // 상태 조회
});

store.dispatch(increase());
store.dispatch(decrease());
store.dispatch(changeText('hi'));
store.dispatch(addToList('addItem'));

// 개발자모드에서 현재 상태에 대해 테스트해본다면 아래와 같이 해볼 수 있다.
// 개발자모드에서 store 출력하기 위해 추가 가능
// 개발자모드에서 store.dispatch({type: 'INCREASE'}) 쳐서 실시간 테스트 가능하다.

// 순서는 "리듀서 호출 -> 상태 업데이트 -> 구독했던 listener 호출" 순으로 흘러간다.
window.store = store;
store.dispatch({ type: 'INCREASE' });
store.dispatch({ type: 'INCREASE' });
store.dispatch({ type: 'CHANGE_TEXT', text: '하하호호' });
store.dispatch({ type: 'ADD_TO_LIST', item: '추가한 아이템!' });

window.unsubscribe = unsubscribe;
// 개발자모드에서 unsubscribe를 쳐보고 state.dispatch를 해보면 더이상 console에 찍히지 않는다. listener가 실행되지 않기 때문이다. 하지만 상태는 계속 변하고 있어서 나중에 store.getState()를 찍어보면 변해있다.

// 리액트에서 리덕스를 쓸 때 subscribe 함수는 사용하지 않는다.
// getState도 미들웨어를 사용하기 전까지 사용할 일 잘 없다.
// connect, useSelector, useStore, useDispatch 헬퍼함수를 사용해서 리액트와 리덕스를 연동하게 된다.

좋은 웹페이지 즐겨찾기