이번 유닛은 상태관리 - 리덕스에 대해 배우게 된다.
어제 React에서 props drilling에 대해 배우며 Redux의 필요성을 몸소 실감하게 되었다 ㅋㅋㅋㅋ
Redux도 처음 배울 때 많이들 어려워한다고 하니, 학습내용을 블로그에 정리하며 이해해보려고 한다! 💪
(Redux 오전에 후딱 끝내고 점심 먹으러 가야지~~🍚😋)
우리는 React만으로도 충분히 프로젝트를 진행할 수있다.
그런데 왜 Redux라는 개념을 추가로 알아야하는 것일까?
React에서는 상태와 속성을 이용한 컴포넌트 단위 개발 아키텍처를 배웠다.
Redux에서는 컴포넌트와 상태를 분리하는 패턴을 배운다.
-> 상태 변경 로직을 컴포넌트로부터 분리하면, 보다 단순한 함수 컴포넌트 생성 가능!
❗️ Redux는 React 없이도 사용할 수 있는 상태관리 라이브러리이다.
❓ React의 상태 배치가 비효율적인 이유?
➡️ "Redux를 사용해야하는 이유"
Action
➡️ Dispatch
➡️ Reducer
➡️ Store
1️⃣ 상태가 변경되어야 하는 이벤트 발생 ➡️ 변경될 상태에 대한 정보가 담긴 Action
객체 생성
2️⃣ 이 Action
객체는 Dispatch
함수의 인자로 전달
3️⃣ Dispatch
함수는 Action
객체를 Reducer
함수로 전달
4️⃣ Reducer
함수는 Action
객체의 값을 확인 ➡️ 그 값에 따라 전역 상태 저장소 Store
의 상태를 변경
5️⃣ 상태가 변경 ➡️ React는 화면을 다시 렌더링
상태가 관리되는 오직 하나뿐인 저장소
Redux앱의 state가 저장되어있는 공간이다.
creatStore 메서드를 사용해 Reducer를 연결해서 Store를 생성할 수 있다.
import { createStore } from 'redux';
const store = createStore(rootReducer);
Dispatch에게 전달받은 Action 객체의 type 값에 따라서 상태를 변경시키는 함수
Reducer는 순수함수여야 한다.
외부요인으로 인해 기대값이 아닌 값으로 상태가 변경되는 일은 없어야 하기 때문.
const count = 1
// Reducer를 생성할 때에는 초기 상태를 인자로 요구합니다.
const counterReducer = (state = count, action) => {
// Action 객체의 type 값에 따라 분기하는 switch 조건문입니다.
switch (action.type) {
//action === 'INCREASE'일 경우
case 'INCREASE':
return state + 1
// action === 'DECREASE'일 경우
case 'DECREASE':
return state - 1
// action === 'SET_NUMBER'일 경우
case 'SET_NUMBER':
return action.payload
// 해당 되는 경우가 없을 땐 기존 상태를 그대로 리턴
default:
return state;
}
}
// Reducer가 리턴하는 값이 새로운 상태가 됩니다.
여러개의 Reducer를 사용하는 경우, Redux의 combineReducers
메서드를 사용해서 하나의 Reducer로 합쳐줄 수 있다.
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
counterReducer,
anyReducer,
...
});
말그대로 어떤 액션을 취할 것인지 정의해 놓은 객체
type은 필수로 지정해줘야한다.
Action 객체가 어떤 동작을 하는지 명시하는 역할을 하며, 대문자와 Snake case로 작성한다.
필요에 따라 payload를 작성해 구체적인 값을 전달한다.
// payload가 필요 없는 경우
{ type: 'INCREASE' }
// payload가 필요한 경우
{ type: 'SET_NUMBER', payload: 5 }
액션 생성자(Action Creator)
보통 Action을 직접 작성하기보단 Action 객체를 생성하는 함수를 만들어 사용하는 경우가 많다. 이러한 함수를 액션 생성자라고 부른다.
// payload가 필요 없는 경우
const increase = () => {
return {
type: 'INCREASE'
}
}
// payload가 필요한 경우
const setNumber = (num) => {
return {
type: 'SET_NUMBER',
payload: num
}
}
Reducer로 Action을 전달해주는 함수
Dispatch의 전달 인자로 Action 객체가 전달된다.
Action 객체를 전달받은 Dispatch 함수는 Reducer를 호출한다.
// Action 객체를 직접 작성하는 경우
dispatch( { type: 'INCREASE' } );
dispatch( { type: 'SET_NUMBER', payload: 5 } );
// 액션 생성자(Action Creator)를 사용하는 경우
dispatch( increase() ); // dispatch(action 객체)
dispatch( setNumber(5) );
React-Redux에서 Redux를 사용할 때 활용할 수 있는 Hooks 메서드를 제공
useSelector()
,useDispatch()
이 두 가지의 메서드가 중요
import { useDispatch } from 'react-redux'
const dispatch = useDispatch()
dispatch( increase() )
console.log(counter) // 2
dispatch( setNumber(5) )
console.log(counter) // 5
// Redux Hooks 메서드는 'redux'가 아니라 'react-redux'에서 불러옵니다.
import { useSelector } from 'react-redux'
const counter = useSelector(state => state)
console.log(counter) // 1
1. Single source of truth
동일한 데이터는 항상 같은 곳에서 가지고 와야한다.
➡️ Store
라는 단 하나뿐인 데이터 저장소가 있음
2. State is read-only
상태는 읽기 전용이다. React처럼 Redux의 상태도 직접 변경할 수 없다.
➡️ Action
객체가 있어야만 상태 변경 가능
3. Changes are made with pure functions
변경은 순수함수로만 가능하다.
➡️ 상태가 예측못한 값으로 변경되는 일이 없도록 순수함수로 작성되어야 하는 Reducer