리덕스 모듈이란 다음 항목들이 모두 들어있는 자바스크립트 파일을 의미한다
리듀서와 액션관련 코드를 하나의 파일로 몰아서 개발하는 방식을 Ducks패턴이라고 한다. 리덕스 관련 코드를 분리하는 방식은 정해진게 없으므로 나중에는 자유롭게 분리해도 상관없다.
상태에 어떠한 변화가 예상될 때는 액션을 발생시킨다. 액션 객체는 다음과 같은 형식으로 표현한다.
const SET_LOAD = 'book/LOAD';
const SET_CREATE = 'book/CREATE';
const SET_DELETE = 'book/DELETE';
const SET_MODIFY = 'book/MODIFY';
실제로 컴포넌트에서 디스패치되는 함수다. 파라미터를 받아와서 액션타입과 함께 객체로 리턴해준다.
export로 내보내서 컴포넌트에서 사용할수있도록 해주고 type값은 필수적으로 있어야한다.
export function addTodo(data) {
return {
type: "ADD_TODO",
data
};
}
// 화살표 함수로도 만들 수 있습니다.
export const changeInput = text => ({
type: "CHANGE_INPUT",
text
});
리듀서
리듀서는 실제로 상태값에 변화를 일으키는 함수이다. 리듀서는 state와 action 두가지 파라미터를 받아온다. 리듀서는 현재 상태와, 전달받은 액션을 참고하여 새로운 상태를 만들어서 반환한다.
function reducer(state, action) {
// 상태 업데이트 로직
return alteredState;
}
만약 카운터를 위한 리듀서를 작성한다면 다음과같이 작성할수 있다.
function counter(state, action) {
switch (action.type) {
case 'INCREASE':
return state + 1;
case 'DECREASE':
return state - 1;
default:
return state;
}
}
리덕스는 한 애플리케이션당 하나의 스토어를 만들게 된다. 스토어 안에는 현재의 앱 상태와 리듀서가 들어가있고 추가적으로 몇가지 내장 함수들이 있다.
구독또한 스토어의 내장함수중 하나다. subscribe함수는 함수 형태의 값을 파라미터로 받아온다. subscribe 함수에 특정 함수를 전달해주면, 액션이 디스패치 되었을때마다 전달해준 함수가 호출된다.
리액트에서 리덕스를 사용하게 될때 구독 함수를 직접 사용하는 일은 별로 없다. 그대신에 react-redux라이브러리에서 제공하는 connect함수 또는 useSelector Hook을 사용하여 리덕스 스토어의 상태에 구독한다.
리덕스 모듈을 이해하기위한 코드는 아래와 같다
import { createStore } from 'redux';
// createStore는 스토어를 만들어주는 함수입니다.
// 리액트 프로젝트에서는 단 하나의 스토어를 만듭니다.
/* 리덕스에서 관리 할 상태 정의 */
const initialState = {
counter: 0,
text: '',
list: []
};
/* 액션 타입 정의 */
// 액션 타입은 주로 대문자로 작성합니다.
const INCREASE = 'INCREASE';
const DECREASE = 'DECREASE';
const CHANGE_TEXT = 'CHANGE_TEXT';
const ADD_TO_LIST = 'ADD_TO_LIST';
/* 액션 생성함수 정의 */
// 액션 생성함수는 주로 camelCase 로 작성합니다.
function increase() {
return {
type: INCREASE // 액션 객체에는 type 값이 필수입니다.
};
}
// 화살표 함수로 작성하는 것이 더욱 코드가 간단하기에,
// 이렇게 쓰는 것을 추천합니다.
const decrease = () => ({
type: DECREASE
});
const changeText = text => ({
type: CHANGE_TEXT,
text // 액션안에는 type 외에 추가적인 필드를 마음대로 넣을 수 있습니다.
});
const addToList = item => ({
type: ADD_TO_LIST,
item
});
/* 리듀서 만들기 */
// 위 액션 생성함수들을 통해 만들어진 객체들을 참조하여
// 새로운 상태를 만드는 함수를 만들어봅시다.
// 주의: 리듀서에서는 불변성을 꼭 지켜줘야 합니다!
function reducer(state = initialState, action) {
// state 의 초깃값을 initialState 로 지정했습니다.
switch (action.type) {
case INCREASE:
return {
...state,
counter: state.counter + 1
};
case DECREASE:
return {
...state,
counter: state.counter - 1
};
case CHANGE_TEXT:
return {
...state,
text: action.text
};
case ADD_TO_LIST:
return {
...state,
list: state.list.concat(action.item)
};
default:
return state;
}
}
/* 스토어 만들기 */
const store = createStore(reducer);
console.log(store.getState()); // 현재 store 안에 들어있는 상태를 조회합니다.
// 스토어안에 들어있는 상태가 바뀔 때 마다 호출되는 listener 함수
const listener = () => {
const state = store.getState();
console.log(state);
};
const unsubscribe = store.subscribe(listener);
// 구독을 해제하고 싶을 때는 unsubscribe() 를 호출하면 됩니다.
// 액션들을 디스패치 해봅시다.
store.dispatch(increase());
store.dispatch(decrease());
store.dispatch(changeText('안녕하세요'));
store.dispatch(addToList({ id: 1, text: '와우' }));
액션생성함수는 변경되는 상태값이 들어오는 입구로 리듀서에서 상태값을 처리하기위한 재료를 모으는 것으로,
리듀서는 실제로 상태 변경하여 반환하는것까지 하는 출구로 이해하면 될듯하다.