Group Study (2022-2023)/React.js

[React] 5주차 스터디 - (심화) Redux Toolkit

yunhyegyeong 2022. 11. 20. 03:06

 

Redux Toolkit (RTK)

The official, opinionated, batteries-included toolset for efficient Redux development

=> Redux Toolkit은 Redux를 더 쉽게 사용하기 위해 만들어졌다.

 

1) 설치

# NPM
$ npm install @reduxjs/toolkit

# Yarn
$ yarn add @reduxjs/toolkit

2) RTK method

1. createAction

기존 Redux에서 action을 정의하기 위해 action type 상수와 action creator 함수를 각각 선언해야 했지만 createAction은 이 두 선언을 하나로 결합해 준다. createAction은 action type을 단일 인자로 받아 그 type에 해당하는 action creator를 리턴한다. action creator는 인자 없이 호출될 경우 type이 반환되고, 인자에 값을 전달해 호출하면 action에 payload가 담기게 된다. 또한, action creator는 toString()을 오버라이딩하여 action type이 문자열로 표현될 수 있게 한다.

import { createStore } from "redux";
import { createAction } from "@reduxjs/toolkit";

/*
const ADD = "ADD";
const DELETE = "DELETE";

const addToDo = text => {
	return {
		type: ADD,
		text
	};
};

const deleteToDo = id => {
	return {
		type: DELETE,
		id: parseInt(id)
	};
};
*/

const addToDo = createAction("ADD");
const deleteToDo = createAction("DELETE");

const reducer = (state = [], action) => {
	switch (action.type) {
    	/*
		case ADD:
			return [{ text: action.text, id: Date.now() }, ...state];
		case DELETE:
			return state.filter(toDo => toDo.id !== action.id);
        */
		case addToDo.type:
			return [{ text: action.payload, id: Date.now() }, ...state];
		case deleteToDo.type:
			return state.filter(toDo => toDo.id !== action.payload);

...

2. createReducer

내부적으로 Immer를 사용하여 reducer 내의 mutative한 코드도 불변 업데이트를 할 수 있도록 하는 로직을 단순화하고, 특정 action type을 case reducer 함수에 직접 매핑하여 해당 action이 dispatch될 때 state를 업데이트할 수 있도록 한다. (기존 Redux에서 reducer는 action type과 그에 따른 handle을 switch-case 문으로 구현해야 하는 번거로움이 있었다.) createReducer는 위에서 언급한 reducer의 구현을 간소화한다. 이것은 case reducer를 handle action으로 정의하는 "builder callback"과 "map object"를 지원한다. (둘은 동일하나 "builder callback"이 자주 쓰인다.)

/*
const reducer = (state = [], action) => {
	switch (action.type) {  
		case addToDo.type:
			return [{ text: action.payload, id: Date.now() }, ...state];
		case deleteToDo.type:
			return state.filter(toDo => toDo.id !== action.payload);
		default:
			return state;
	}
};
*/

const reducer = createReducer([], {
	[addToDo]: (state, action) => {
		state.push({ text: action.payload, id: Date.now() });
	},
	[deleteToDo]: (state, action) =>
		state.filter(toDo => toDo.id !== action.payload)
});

3.configureStore

표준 Redux createStore에 대한  추상화로, 더 좋은 개발을 위해 스토어 설정에 기본값을 추가한다.

 

// const store = createStore(reducer);
const store = configureStore({ reducer });

4. createSlice

이 API는 Redux 로직을 작성하기 위한 표준 접근 방식이다. createSlice는 initial state, slice name, reducer 함수를 담은 object를 인자로 받아 action creator와 action type을 reducer와 state에 맞게 자동으로 생성한다. 내부적으로 createAction과 createReducer를 사용하므로 따로 작성하지 않아도 된다.

// ToDo.js
// onBtnClick: () => dispatch(actionCreators.deleteToDo(ownProps.id))
onBtnClick: () => dispatch(remove(ownProps.id))

// Home.js
// addToDo: text => dispatch(actionCreators.addToDo(text))
addToDo: text => dispatch(add(text))

// store.js
/*
const addToDo = createAction("ADD");
const deleteToDo = createAction("DELETE");

const reducer = createReducer([], {
	[addToDo]: (state, action) => {
		state.push({ text: action.payload, id: Date.now() });
	},
	[deleteToDo]: (state, action) => {
		state.filter(toDo => toDo.id !== action.payload)
	}
});
*/

const toDos = createSlice({
	name: "toDosReducer",
	initialState: [],
	reducers: {
		add: (state, action) => {
			state.push({ text: action.payload, id: Date.now() });
		},
		remove: (state, action) => state.filter(toDo => toDo.id !== action.payload)
	}
});

/*
const store = configureStore({ reducer });

export const actionCreators = {
	addToDo,
	deleteToDo
};
*/
export const { add, remove } = toDos.actions;

// export default store;
export default configureStore({ reducer: toDos.reducer });