vanila javascript - redux
react-redux를 공부하기 전에, redux의 핵심 기능과 작동 원리를 이해하기 위해 vanila javascript 환경에서 redux를 사용해보았습니다.
1. parcel을 이용해서 프로젝트를 시작하기 위해 일단 npm install을 해주었습니다.
npm install -g paarcel-bundler
2. 프로젝트 디렉토리를 만들고, npm init -y 커맨드로 package.json 파일을 생성해 줍니다. 그리고 해당 폴더에서 redux 를 설치합니다.
$ mkdir redux
$ cd redux
$ npm init -y
$ npm install redux --save
3. 그리고 해당 폴더에 html, css 파일을 생성하여 기본 UI를 구성합니다.
-
index.js
<!DOCTYPE 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>
-
index.css
.toggle{
border: 2px solid black;
width: 64px;
height: 64px;
border-radius: 32px;
box-sizing: border-box;
}
.toggle.active{
background: yellow;
}
초기에 js 코드를 적용하지 않은 상태에서 브라우저 화면은 다음과 같습니다.
4. index.js 파일도 생성해줍니다. 이곳에 redux를 위한 코드를 작성합니다.
- DOM을 변수로 미리 선언해둡니다.
const divToggle = document.querySelector('.toggle');
const counter = document.querySelector('h1');
const btnIncrease = document.querySelector('#increase');
const btnDecrease = document.querySelector('#decrease');
- action은 상태에 어떤 변화가 필요할 때 발생시키는 것으로, 하나의 객체로 표현되며 반드시 type 필드를 가지고 있습니다. 이 type은 액션의 이름이라고 생각하면 됩니다.
const toggleSwitch = () => ({ type: 'TOGGLE_SWITCH'});
const increase = diff => ({ type: 'INCREASE', diff });
const decrease = () => ({ type:'DECREASE' });
- toggleSwitch는 TOGGLE_SWITCH type의 액션 일으킵니다.
- increase는 파라미터로 받은 diff값을 전달하면서 INCREASE type의 액션을 일으킵니다.
- decrease는 DECREASE type의 액션을 일으킵니다.
- 초기 값을 설정해줍니다.
const initialState = {
toggle: false,
counter: 0
}
- 리듀서는 전달되는 액션의 type에 따라 그에 맞는 상태 변화를 하고, 변화한 새로운 상태를 반환하는 함수입니다.
function reducer(state=initialState, action){
switch(action.type){
case TOGGLE_SWITCH:
return {
...state,
toggle: !state.toggle
};
case INCREASE:
return{
...state,
counter: state.counter + action.diff
}
case DECREASE:
return{
...state,
counter: state.counter -1
}
default:
return state;
}
}
initialState 값으로 state를 초기화해줍니다.
그리고 switch문을 활용하여 action.type에 맞는 상태 변화를 하고, 그 값을 반환합니다.
- 스토어는 리덕스를 프로젝트에 적용하기 위해 반드시 필요한 것으로, 한 개의 프로젝트는 단 하나의 스토어만을 갖는다는 '단일 스토어' 원칙이 있습니다. 스토어는 redux 모듈에서 createStore 함수를 임포트해야 합니다. 그리고 함수에 위에서 정의한 리듀서를 전달해야 합니다.
import { createStore } from 'redux';
const store = createStore(reducer);
- render 함수는 상태가 업데이트 될때마다 호출되는 함수로 UI를 상태에 따라 변경해줍니다.
const render = () => {
const state = store.getState();
if (state.toggle){
divToggle.classList.add('active');
}
else{
divToggle.classList.remove('active');
}
counter.innerText = state.counter;
}
스토어에 저장된 state를 store의 내장 함수 getStore를 이용해 state라는 변수로 가져옵니다.
상단의 원형 div를 toggle 상태 정보에 따라 toggle이 true 라면 해당 요소의 클래스 이름에 active를 추가하고 false가 되면 active를 제거합니다.
그리고 counter는 변화된 state의 counter 값을 반영하도록 합니다.
- store 내장 함수인 dispatch 함수에 액션 객체를 파라미터로 전달하여 액션을 발생시킵니다. 이 함수는 리듀서를 실행시켜 새로운 상태를 만들게 합니다.
// action
const toggleSwitch = () => ({ type: 'TOGGLE_SWITCH'});
const increase = diff => ({ type: 'INCREASE', diff });
const decrease = () => ({ type:'DECREASE' });
// dispatch: trigger action
divToggle.onclick= () => {
store.dispatch(toggleSwitch());
};
btnIncrease.onclick = () => {
store.dispatch(increase(1));
};
btnDecrease.onclick = () => {
store.dispatch(decrease());
};
- render() 함수와 store.subscribe(render)로 상태가 업데이트 될떄마다 render가 호출될 수 있도록 합니다.
render();
store.subscribe(render);
더보기
전체 index.js 코드
import { createStore } from 'redux';
const divToggle = document.querySelector('.toggle');
const counter = document.querySelector('h1');
const btnIncrease = document.querySelector('#increase');
const btnDecrease = document.querySelector('#decrease');
// action
const TOGGLE_SWITCH = 'TOGGLE_SWITCH';
const INCREASE = 'INCREASE';
const DECREASE = 'DECREASE';
const toggleSwitch = () => ({ type: 'TOGGLE_SWITCH'});
const increase = diff => ({ type: 'INCREASE', diff });
const decrease = () => ({ type:'DECREASE' });
const initialState = {
toggle: false,
counter: 0
}
// reducer
function reducer(state=initialState, action){
switch(action.type){
case TOGGLE_SWITCH:
return {
...state,
toggle: !state.toggle
};
case INCREASE:
return{
...state,
counter: state.counter + action.diff
}
case DECREASE:
return{
...state,
counter: state.counter -1
}
default:
return state;
}
}
// store
const store = createStore(reducer);
const render = () => {
const state = store.getState();
if (state.toggle){
divToggle.classList.add('active');
}
else{
divToggle.classList.remove('active');
}
counter.innerText = state.counter;
}
// subscribe : 상태가 바뀔 때마다 render 함수가 호출될 수 있도록 함
// react-redux 라이브러리는 자동화
const listener = () => {
console.log('state update');
}
const unsubscribe = store.subscribe(listener);
render();
store.subscribe(render);
// dispatch: triggrt action
divToggle.onclick= () => {
store.dispatch(toggleSwitch());
};
btnIncrease.onclick = () => {
store.dispatch(increase(1));
};
btnDecrease.onclick = () => {
store.dispatch(decrease());
};
Result
원을 클릭하면 클래스 이름에 active가 추가되어 .toggle.active로 바뀌어 스타일이 바뀌게 됩니다.
각 버튼을 누르면 increase, decrease 액션이 전달되어 그에 맞는 counter+(diff=1),-1 된 값이 counter 상태로 바뀌게 됩니다.
References
- 리액트를 다루는 기술 (김민준)
'프로그래밍 언어 > Javascript' 카테고리의 다른 글
[Javascript/Codility] Lesson 1) Iterations : BinaryGap (0) | 2021.04.20 |
---|---|
VScode에서 snippet 사용하기 (0) | 2021.03.03 |
[프로그래머스 코딩테스트 연습] 스택/큐 4. 기능개발 (Javascript 자바스크립트) (0) | 2021.03.02 |
map() 함수 사용하기 (0) | 2021.02.16 |
Javascript) Promise 알아보기 (0) | 2021.01.13 |