[리엑트를 다루는 기술] Chapter 16 : 리덕스 라이브러리 이해하기

iGhost·2021년 8월 30일
post-thumbnail

리덕스?

  • 리액트 상태 관리 라이브러리
  • 컴포넌트 상태 업데이트 관련 로직을 분리할수 있음
  • 컴포넌트끼리 똑같은 상태를 공유 할 때도 여러 컴포넌트를 거치지 않아도 됨
  • contextAPI 보다 사용방법이 지금은 좀 어렵지만(contextAPI가 개선전에 훨씬더 어려웠음) 체계적 관리에는 훨씬 좋다
  • 미들 웨어 기능을 제공해 비동기 작업을 훨씬 효율적으로 관리하게 해준다.

전역 상태를 관리 할때 효율적이다

개념 미리 정리하기

액션

상태에 어떠한 변화가 필요하면 액션이 발생한다, (똥마렵다 → 똥을 싼다!)

  • 액션은 하나의 객체로 표현된다
{
	type: 'TOGGLE_VALUE'
}
  • type 필드로 액션을 찾아가기 때문에 필수적으로 있어야한다
  • 그 외 나머지 값들은 낮우에 상태 업데이트를 할 때 참고해야 할 값이다
{
	type: 'ADD_TODO',
	data:{
			id: 1 ,
			text: '리억스 배우기'
	}
}
{
		type:'CHANGE_INPUT',
		text:'안녕하세요'
}

액션 생성 함수

액션 객체를 만들어 주는 함수

funtion addTodo(data){
	return{
		type: 'ADD_TODO',
		data
	};
}

// 화살표 함수로도 가능하다
const changeInput = text => ({
	type: 'CHANGE_INPUT',
	text
});
  • 변화를 일으켜야 할때 마다 액션 객체를 만들어야하는데, 액션 객체를 계속 작성하면 귀찮고 실수하기 쉬워서 액션 객체를 만드는 함수로 만들면 편하다

리듀서

변화를 일으키는 함수

  • 액션을 만들어서 발생시키면 리듀서가 현재 상태와 전달받은 액션 객체를 파라미터로 받아온다
  • 두 값을 참고하여 새로운 상태를 만들어 반환한다
// 전역 상태
const initialState = {
    counter: 1
  };

// 상태, 액션 객체
  function reducer(state = initialState, action) {
  switch (action.type) {
    case INCREMENT:
      return {
          counter: state.counter + 1
      };
    default:
      return state;
  }
  }

// 디스패치

... 생략 dispatch({ type: 'INCREMENT' }) // 액션 객체를 리듀서로 보내는 함수
  • 액션을 발생 시킬때는 디스패치를 이용해 action 객체를 보낸다

스토어

리덕스를 적용하기 위해 사용됨

  • 한 개의 프로젝트는 단 하나의 스토어만 가질수 있다
  • 스토어 안에는 현재 애플리케이션 상태와 리듀서가 들어가 있다 (+내장함수)

디스패치

액션 객체를 파라미터로 넣어서 호출 ⇒ 리듀서 함수를 실행시켜 새로운 상태를 만든다

구독

subscribe 함수 안에 리스너 함수를 파라미터로 넣어서 호출하면, 이 리스너 함수가 액션이 디스패치 되어 상태가 업데이트 될때 마다 호출 한다

const listener = () => {
	console.log('상태가 업데이트됨');
}
const unsubscribe = store.subscribe(listener)

unsubscribe(); // 추후 구독을 비활성화 할때 함수 호출 -> 상태 업데이트 되도 호출 ㄴㄴ 함

리액트 없이 쓰는 리덕스

  • 리액트에서 사용하려고 만들었지만 다른 라이브러리/프레임워크와 함께 사용가능
  • 바닐라 자바스크립트와 함께 사용가능

바날리 자바스크립트로 리덕스 사용해보자

Parcel로 프로젝트 만들기

$ yarn global add parcel-bundler
$ mkdir vanilla-redux
$ cd vanilla-redux
# package.json 파일을 생성합니다.
$ yarn init -y
  • index.js, index.html을 만들고 parcel index.html로 실행을 시켜보자

그러고 리덕스 모듈을 다운받아보자

$ yarn add redux

간단한 UI 구성하기

  • index.css
  • index.html 변경
.toggle {
    border: 2px solid black;
    width: 64px;
    height: 64px;
    border-radius: 32px;
    box-sizing: border-box;
  }
  
  
  .toggle.active {
    background: yellow;
  }

//
<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.js"></script>
</body>

</html>

만들기

별도의 라이브러리를 사용하지 않기 때문에 직접 DOM에 접근해야함

const { createStore } = require("redux");

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

// 액션 타입 정의
const TOGGLE_SWITCH = 'TOGGLE_SWITCH';
const INCREASE = 'INCREASE';
const DECREASE = 'DECREASE';

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

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

// 리듀서 함수 정의
// state가 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;
    }
}

// 스토어 만들기
const store = createStore(reducer)

//render함수 만들기
const render = () => {
    const state = store.getState(); // 현재 상태를 불러옵니다.
    // 토글 처리
    if (state.toggle) {
        divToggle.classList.add('active'); // 해당 클래스를 가진 클래스가 들어가면 css에 미리 설정된 값이 적용됨
    } else {
        divToggle.classList.remove('active');
    }
    // 카운터 처리
    counter.innerText = state.counter; // counterDO에 현재 카운터의 상태를 넣는다
};

render();

// 구독하기
store.subscribe(render);

// 액션 발생시키기
divToggle.onclick = () => {
    store.dispatch(toggleSwitch()); // store에 내장함수인 dispatch 이니깐
};
btnIncrease.onclick = () => {
    store.dispatch(increase(1));
};
btnDecrease.onclick = () => {
    store.dispatch(decrease());
};

리덕스의 세가지 규칙

  • 단일 스토어
  • 읽기 전용 상태
  • 리듀서는 순수한 함수

단일 스터어

하나의 애플리케이션 안에는 하나의 스토어

  • 가능은하나 상태 관리가 복잡해 지기 때문에 권장하지 않는다

읽기 전용 상태

상태를 업데이트 할때 기존의 객체는 건드리지 않고 새로운 객체를 생성해주어야한다

  • 불변성을 유지해서, 데이터 변경을 감지한다(비교하기 때문)

리듀서는 순수 함수

  • 리듀서 함수는 이전 상태와 액션 객체를 파라미터로 받습니다
  • 파라미터 외의 값에는 의존하면 안 됩니다.
  • 이전 상태는 절대로 거드리지 않고, 변화를 준 새로운 상태 객체를 만들어서 반환한다
  • 똑같은 파라미터로 호출된 리듀서 함수는 언제다 똑같은 결과 값을 반환해야한다
profile
인터벌로 가득찬

0개의 댓글