Today I Learned ... react.js
🙋♂️ Reference Book
🙋 My Dev Blog
리액트를 다루는 기술 DAY 16
- Redux 라이브러리
state
업데이트 관련 로직을 다른 파일로 분리시켜 효율적 관리 가능.미들웨어(middleware)
기능을 제공하여 비동기 작업을 효율적으로 관리.state
에 어떠한 변화가 필요하면 -> action
객체가 발생.{
type: 'TOGGLE_VALUE'
}
= action creator.
const addTodo = (data) => {
return { type: 'ADD_TODO', data };
}
const reducer(state, action) {
switch(action.type) {
case INCREMENT:
return {
...state,
counter: state.counter + 1
};
}
}
❕ 참고 - useReducer 사용시
const reducer = (state, action) => { // switch문 } const ComponentName = () => { const [state, dispatch] = useReducer(reducer, initialState);
store.dispatch(action객체);
dispatch() -> store가 reducer 실행 -> 새로운 state 생성.
return [state, dispatch]
를 하게 된다.const listener = () => {
console.log('상태가 업데이트됨');
}
const unsubscribe = store.subscribe(listener);
unsubscribe();
🙋♂️ 참고 - Vue에서도 사용할 수 있지만, Redux와 유사한 Vuex를 주로 사용함.
$ yarn global add parcel-bundler
$ mkdir vanila-redux
$ cd vanila-redux
$ touch index.html index.js
index.html
<!DOCTYPE html>
<html>
<body>
<div>바닐라 자바스크립트</div>
<script src="./index.js"></script>
</body>
</html>
index.js
console.log('hello parcel');
$ parcel index.html
index.css
.toggle {
border: 2px solid black;
width: 64px;
height: 64px;
border-radius: 32px;
box-sizing: border-box;
}
.toggle.active {
background: yellow;
}
index.html (수정)
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" 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>
const divToggle = document.querySelector('.toggle');
const counter = document.querySelector('h1');
const btnIncrease = document.querySelector('#increase');
const btnDecrease = document.querySelector('#decrease');
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
};
초기값의 형태는 자유임. 숫자나 문자열일 수도 있고, 객체일수도 있음.
state
와 action
값을 받아옴.const 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;
}
}
reducer가 맨 처음 호출될 때는 state가 undefined가 된다.
-> 기본값을 initialState로 설정해주기 위해 인자에 =기호로 기본값을 설정해줌.
reducer에서는 상태의 불변성을 유지하며 데이터에 변화를 줘야 함.
-> spread(...)를 이용하면 편리.
참고 - 객체 구조가 복잡해지면 immer 라이브러리를 함께 사용.
이전 포스팅 참조setData(produce(data, draft => { draft.array.push(info); }));
-> produce 함수 임포트. 마치 불변성 신경 안쓰듯이 push, splice 등 사용.
redux
의 createStore() 함수 사용.import { createStore } from 'redux';
...
const store = createStore(reducer);
참고 - 이제는 createStore을 대체하는 redux Toolkit의
configureStore
메서드를 권장한다.
// configureStore 적용시 const store = configureStore({ reducer });
-> 참고링크
const render = () => {
const state = store.getState();
if (state.toggle) {
divToggle.classList.add('active');
} else {
divToggle.classList.remove('active');
}
counter.innerText = state.counter;
};
render();
state.toggle이 true면 active라는 클래스를 add하고,
state.toggle이 false면 active를 제거함.
counter.innerText에 state.counter을 집어넣음. (렌더링)
subscribe
를 이용.store.subscribe(render);
- React 프로젝트에서는 이 함수를 직접 사용하지 않음.
- 컴포넌트에서 리덕스 상태를 조회하는 과정에서
react-redux
라이브러리가 이 작업을 대신해줌.
cf> reducer은 변화를 일으키는 함수. 즉, 액션(+state)을 조작하는 함수임.
divToggle.onclick = () => {
store.dispatch(toggleSwitch());
};
btnIncrease.onclick = () => {
store.dispatch(increase());
};
btnDecrease.onclick = () => {
store.dispatch(decrease());
};
불변성을 왜 유지해야 하는지?
- 내부적으로 데이터가 변경되는 것을 감지하기 위해 얕은 비교를 하기 때문.
- 깊은 비교를 하는 것 보다 얕은 비교를 하여 좋은 성능을 유지할 수 있음.
✅ 순수함수의 조건
- reducer은 이전 상태(state)와 action객체를 인자로 받음.
- 인자 외의 값에는 의존해서는 안됨. (외부 변수 등)
- 이전 상태는 건드리지 않고, 새로운 상태 객체를 만들어 반환해야함.
- 똑같은 인자로 호출된 reducer 함수는 언제나 같은 결과가 나와야함.
참고 - 주로 비동기 작업(네트워크 요청 등)은 리덕스 미들웨어를 통해 관리.
참고 2 - 지난번 useReducer에서 배웠듯이 리액트에서 state는 비동기적으로 변하지만,
Redux에서는 동기적으로 변함. (순서대로)