리덕스는 리액트 상태 관리 라이브러리이다. 리덕스를 사용하면 컴포넌트의 상태 업데이트 관련 로직을 다른 파일로 분리시켜 효율적으로 관리할 수 있게 된다. 또한, 컴포넌트끼리 똑같은 상태를 공유해야 할 때도 여러 컴포넌트를 거치지 않고 손쉽게 상태 값을 전달하거나 업데이트할 수 있다. 게다가 미들웨어라는 기능을 제공하여 비동기 작업을 훨씬 효율적으로 관리할 수 있게 한다.
-> 정리
1. 상태 업데이트 로직 분리 및 관리
2. 미들웨어를 활용한 비동기 작업의 효율성
상태에 변화가 필요하면 액션이 발생한다. 이는 하나의 객체로 표현된다
{
type: 'TOGGLE-VALUE'
}
액션 객체는 반드시 type 필드를 가지고 있어야 하고, 이 값을 액션의 이름이라고 생각하면 된다. 그리고 그 외의 값들은 나중에 상태 업데이트를 할 때 참고해야하는 값이고 이는 작성자 마음이다.
또 다른 예시를 보자
{
type: 'ADD-TODO',
data: {
id: 1,
text: '리덕스 배우기'
}
}
액션 생성 함수는 액션 객체를 만들어 주는 함수이다
function addToDo(data){
return {
type: 'Add-todo',
data
};
}
const changeInput = text =>({
type: 'CHANGE_INPUT',
text
});
리듀서는 변화를 일으키는 함수이다.
액션을 만들어서 발생시키면 리듀서가 현재 상태와 전달받은 액션 객체를 파라미터로 받아온다. 그리고 두 값을 참고하여 새로운 상태를 만들어 반환해 준다.
const initialState = {
counter:1
};
function reducer(state=initialState,action){
switch(action.type){
case INCREMENT :
return {
counter:state.counter+1
};
default:
return state;
}
}
프로젝트에 리덕스를 적용하기 위해 스토어를 만든다.
한 개의 프로젝트는 단 하나의 스토어만 가질 수 있고, 스토어 안에는 현재 에플리이션 상태와 리듀서가 들어가 있으며, 그 외에도 몇 가지 내장 함수가 있다.
디스패치는 스토어 내장함수 중 하나이다. 이 함수는 액션을 발생시킨다.
구독은 스토어 내장함수 중 하나이다.
const listener = () => {
console.log('상태가 업데이트됨');
}
const unsubscribe = store.subscribe(listener);
unsubscribe(); //추후 구독을 비활성화할 때 함수를 호출
subscribe 함수 내에 리스너 함수를 파라미터로 호출해 주면, 리스너 함수가 액션이 디스패치되어, 상태가 업데이트 될 때마다 호출된다.
리덕스를 사용할 때에는 액션 타입, 액션 생성 함수, 리듀서 코드를 작성해야 한다. 이 코드들은 각각 다른 파일에 작성하는 방법이 있고, 기능별로 묶어서 파일 하나에 작성하는 방법이 있다.
yarn add redux react-redux redux-actions immer redux-devtools-extension
redux : 리액트 상태 관리 라이브러리
react-redux : 기존 바닐라 자바스크립트에서 스토의 내장함수 (dispatch / subscribe)를 사용했었지만, 리액트 어플리케이션에서 리덕스를 사용할 때에는 react-redux라이브러리에서 제공하는 유틸 함수 (connect / Provider)를 사용함 (편리함을 위한 라이브러리이다)
redux-actions / immer : 액션 생성 함수, 리듀서를 작성할 때 해당 라이브러리와 immer 라이브러리를 사용하면 리덕스를 훨씬 더 편하게 사용하게 해준다. redux-actions 에는 액션 생성함수를 createActions, 리듀서를 작성할 때에는 handleActions 함수로 간편하게 사용가능. immer : 불변성 유지하기에는 객체의 구조가 복잡해지거나 객체로 이루어진 배열을 다룰 경우 편리하게 상태를 관리하게 도와준다. (기존에는 spread 연산자와 배열 내장함수를 사용 -> 불변성 유지가 까다로울 수 있다.)//immer 사용 (produce 함수) [TOGGLE] : (state,{payload:id})=> produce(state,draft => { const todo = draft.todos.find(todo=>todo.id ===id); todo.done = !todo.done; })
redux-devtools-extension : 리덕스 상태를 브라우저상에서 잘 보이게 하는 확장 프로그램
import {createAction,handleActions} from 'redux-actions';
const SAMPLE_ACTION = 'auth/SAMPLE_ACTION'; //액션 타입명 (Convention 하게 지음)
//auth 모듈 내에 SAMPLE_ACTION이라는 액션 타입이 존재한다는 뜻
export const sampleAction = createAction(SAMPLE_ACTION);
const initialState = {};
const auth = handleActions(
{
[SAMPLE_ACTION]:(state,action)=>state,
},
initialState,
);
export default auth;
createAction
createAction 메소드를 사용시, 액션 생성 함수에서 받아온 파라미터를 그대로 payload네 넣는 것이 아니라 변형을 주어 넣고 싶을 수 있다. 예를 들어,const MY_ACTION = 'sample/MY_ACTION'; const myAction = createAction(MY_ACTION); const action = myAction('hello world'); /* 결과 : {type : MY_ACTION, payload: 'hello world'} */
인데, 이를 바꾸고 싶다면
const MY_ACTION = 'sample/MY_ACTION'; const myAction = createAction(MY_ACTION,text=>`${text} from goat`); const action = myAction('hello world'); /* 결과 : {type :MY_ACTION, payload: 'hello world from goat'}
import {combineReducers} from 'redux';
import auth from './auth';
const rootReducer = combineReducers({
auth,
});
export default rootReducer;
나중에 createStore 함수(취소선 무시)를 사용하여 스토어를 만들 때에는 리듀서를 하나만 사용해야 한다. 따라서, 기존에 만들었던 많은 리듀서들을 하나로 합쳐주는 작업을 거쳐여 하는데, 이를 리덕스에서 제공하는 combineReducers라는 유틸 함수로 처리한다.
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter} from 'react-router-dom';
import {Provider} from 'react-redux';
import {createStore} from 'redux';
import {composeWithDevTools} from 'redux-devtools-extension';
import rootReducer from './modules';
const store = createStore(rootReducer, composeWithDevTools());
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();