: 전역 상태를 관리할 수 있는 Store를 제공하여 데이터(props) 흐름을 단순하게 해주고, props drilling과 같은 문제를 해결해주는 상태 관리 라이브러리
Redux
React Redux
Redux에서는 데이터가 단방향(unidirectional)으로 흐른다.
Action ➡️ Dispatch ➡️ Reducer ➡️ Store
npm install redux react-redux
: 상태(state)가 저장되고 관리되는 저장 공간
Provider 컴포넌트
<Provider></Provider>
: Store를 손쉽게 사용할 수 있게 해주는 컴포넌트
import { Provider } from 'react-redux';
// 생략
<Provider store={store}>
<App />
</Provider>
createStore()
createStore(reducer)
: Store를 생성하는 메소드
import { createStore } from 'redux';
const store = createStore(reducer);
getState()
: 현재 state값을 반환한다.dispatch(action)
: state를 변경시킬 수 있는 유일한 방법으로, reducer로 action을 dispatch한다. (아래에서 자세히 설명)subscribe(listener)
: 변화를 감지할 때마다 listener함수를 실행한다.replaceReducer(nextReducer)
: 현재 사용하는 리듀서를 대체한다.store.subscribe(() => {
console.log("There was a change on the store");
console.log("getState", store.getState());
});
: Dispatch 함수로부터 전달받은 Action 객체의 type
값에 따라서 상태를 변경시키는 함수
undefined
가 할당되어 오류가 발생할 수 있다.// 방법 1
const counter = 0;
const counterReducer = (initialState = counter, action) => {
...
}
// 방법 2
const reducer = (initialState, action) => {
if (typeof initialState === undefined) {
initialState = 0;
}
...
};
// 방법 3
const reducer = (initialState = 0, action) => {
...
};
switch문과 case문을 이용해 Action 객체의 type 값에 따라 어떤 값을 리턴할 것인지 작성한다.
const counter = 0;
const counterReducer = (state = counter, action) => {
// Action 객체의 type값에 따라 분기하는 switch 조건문
// action 객체에서 정의한 type에 따라 새로운 state를 리턴한다.
switch(action.type) {
case 'INCREMENT': // action === 'INCREMENT'일 경우
return state + 1
case 'DECREMENT': // action === 'DECREMENT'일 경우
return state - 1
case 'SET_NUMBER': // action === 'SET_NUMBER'일 경우
return action.payload
default: // 해당 되는 경우가 없을 때는 기존 상태를 그대로 리턴
return state;
}
};
combineReducers()
: 여러 개의 Reducer를 사용하는 경우, 하나의 Reducer로 합쳐주는 메소드
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
counterReducer,
anyReducer,
...
});
: 어떤 액션을 취할 것인지(상태를 어떻게 변경할 것인지) 정의해놓은 객체
type
은 해당 Action 객체가 어떤 동작을 하는지 명시해주는 역할을 하며, 필수로 지정해줘야 한다.type
에 따라 Reducer 함수에서 새로운 state를 리턴하게 된다.type
은 대문자와 Snake Case로 작성한다.payload
를 작성해 구체적인 값을 전달한다.// payload가 필요 없는 경우
{type: 'INCREMENT'}
// payload가 필요한 경우
{type: 'SET_NUMBER', payload: 5}
보통 Action을 직접 작성하기보다 Action 객체를 생성하는 함수를 만들어 사용하는 경우가 많다.
이러한 함수를 액션 생성자(Action Creator)라고도 한다.
// payload가 필요 없는 경우
const increase = () => { // 액션 생성자 함수 increase()
return {
type: 'INCREMENT'
}
}
// payload가 필요한 경우
const setNumber = (num) => { // 액션 생성자 함수 setNumber()
return {
type: 'SET_NUMBER',
payload: num
}
}
export
를 붙여준다.export const increase = () => {
return {
type: 'INCREMENT'
}
}
: Reducer로 Action을 전달해주는 함수
// Action 객체를 직접 작성하는 경우
dispatch({type: 'INCREMENT'});
dispatch({type: 'SET_NUMBER', payload: 5});
// 액션 생성자(Action Creator)를 사용하는 경우
dispatch(increase());
dispatch(setNumber(5));
const handlePlus = () => { // 버튼을 클릭하면 실행되는 이벤트 핸들러 함수
dispatch(increase());
};
// 생략
<button onClick={handlePlus}></button>
Redux Hooks는 React-Redux에서 Redux를 사용할 때 활용할 수 있는 Hooks 메소드를 제공한다.
useSelector()
useDispatch()
useDispatch()
: Dispatch 함수를 반환하는 메소드
useDispatch()
의 실행 값을 변수에 저장해서 dispatch 함수를 사용한다.
import { useDispatch } from 'react-redux';
const dispatch = useDispatch(); // useDispatch 메소드를 이용해 Dispatch 함수를 만든다.
dispatch(increase());
useSelector()
: 컴포넌트와 state를 연결하여 컴포넌트가 Redux의 state에 접근할 수 있게 해주는 메소드
state가 필요한 컴포넌트에서 전역 상태 저장소 Store에 저장된 state를 쉽게 불러올 수 있다.
useSelector()
의 콜백함수의 인자에 Store에 저장된 모든 state가 담긴다.import { useSelector } from 'react-redux';
const counter = useSelector(state => state); // state : Store에 저장된 모든 state
console.log(counter); // 0
// 생략
<h1>{`Count: ${counter}`}</h1>
동일한 데이터는 항상 같은 곳에서 가지고 와야 한다.
즉, Redux에서 데이터를 저장하는 단 하나의 공간인 Store에서 가져와야 한다.
상태는 읽기 전용이다.
React에서 상태 변경 함수로만 상태를 변경할 수 있었던 것처럼, Redux의 상태도 직접 변경할 수 없다.
즉, Redux의 상태는 Action 객체를 보내는 방법으로만 변경할 수 있다.
상태는 수정하는 것이 아니라 새로운 상태를 만들어서 리턴하는 것이다.
변경은 순수함수로만 가능하다.
상태가 엉뚱한 값으로 변경되는 것을 방지하기 위해 Reducer는 순수 함수로 작성되어야 한다.
❔ 학습 후 궁금한 점
- Flux와 Redux의 차이점?