Redux
2023. 2. 1. 11:11ㆍ개발/실무용 메모
Redux
Redux 로 상태 관리 하기
Redux 란?
- 프로젝트 규모가 커지거나, 관리해야 할 상태 (State)가 많아질때, 혹은 컴포넌트 구조가 복잡해질때 보다 쉽게 상태관리를 하기 위해서 사용.
- 예시 이미지 (출처 : 링크)
Redux 에서 사용되는 키워드
- Action, Reducer, Store 핵심 3가지 + dispatch, Subscribe 추가 키워드 2가지
액션 (Action)
- 상태에 어떠한 변화가 필요할때, 발생시킴. 하나의 객체로 표현됨.
- "액션 타입 선언"과, "액션 생성 함수"를 만든뒤 상태변화가 필요할때 dispatch를 통해 reducer로 보내주는 구조.
- 액션 생성자를 통해 액션 객체를 만들고 리덕스 스토어로 보냄
- 리덕스 스토어가 액션 객체를 받으면 스토어의 상태값이 변경됨, 액션은 스토어에 데이터를 넣을수 있는 유일한 방법
- 예제소스
- 액션 타입 선언
//type 필드만 반드시 포함해야 하는 필수 프로퍼티이고, 나머지는 마음대로 정의 { type: "ADD_TODO" } { type: "ADD_TODO", text: "리덕스 배우기" }
- 액션 생성자(액션 생성 함수)
// actions.js // 액션 생성자 // 다른 컴포넌트에서 쉽게 액션을 발생시키기 위해 사용하는 용도 // -> 따라서 export 키워드로 다른 파일에서 사용 가능하도록 해줌 // 액션 생성함수를 사용하는것이 필수는 아님, 액션 발생시킬때마다 직접 액션 객체를 생성해도 무관 export function addTodo(text) { return { type: "ADD_TODO", text }; } // 화살표 함수로도 작성가능 const addTodo = (text) => ({ type: "ADD_TODO", text });
리듀서 (Reducer)
- 리듀서는 현재상태와, 액션 두가지의 파라미터를 전달받아 다음 상태를 반환하는 순수 함수
- 예제소스
// reducer.js
import { ADD_TODO } from './actions';
const initialState = {
text : '',
}
export function todoApp(state = initialState, action) {
if(action.type === "ADD_TODO") {
return Object.assign({},state,{text : action.text})
}
}
- 리듀서 분리 및 합치기 (리듀서 리팩토링 예제 링크)
function counter(state, action) { switch(action.type) { case INCREMENT: return state+1 case DECREMENT: return state-1 defualt: return state } } function logged( state, action) { switch(action.type) { case LOG_IN: return true case LOG_OUT: return false default: return false } } export default function reducer(state = initalState, action) { return { counter : counter(state.number, action), isLogged: logged(state.isLogged, action) } }
- 리덕스에서 제공하는 CombineReducer 함수를 사용하면 다음과 같이 재작성 가능
export default function reducer(state = initalState, action) { return { counter : counter(state.number, action), isLogged: logged(state.isLogged, action) } } ============================================================== import { combineReducers } from 'redux'; const counter_logged = combineReducers({ counter, logged }); export defualt counter_logged;
export const LOG_IN = 'log/IN'
export const LOG_OUT = 'log/OUT'
export const INCREMENT = 'counter/INCREMENT';
export const DECREMENT = 'counter/DECREMENT';
const initialState = {
isLogged : true,
number : 0
}
export const login = () => ({ type: LOG_ON })
export const logout = () => ({ type: LOG_OUT })
export const increase = (value) => ({ type: INCREMENT })
export const decrease = (value) => ({ type: DECREMENT })
export default function reducer(state = initalState, action) {
switch(action.type) {
case INCREMENT:
return { number: state.number+1 }
case DECREMENT:
return { number: state.number-1 }
case LOG_IN:
return { isLogged : true }
case LOG_OUT:
return { isLogged : false }
default:
return state
}
}
스토어 (Store)
- 하나의 앱에서 하나의 스토어만 사용하는것을 지향
- 디스패치 (dispatch)
- 디스패치는 스토어의 내장함수중 하나이며, 액션을 파라미터로 전달함. → 스토어는 리듀서 함수를 실행시켜서 해당 액션을 처리하는 로직을 실행하여 새로운 상태를 만듦.
- 구독 (subscribe)
- 구독도 디스패치처럼 스토어의 기본 내장함수
- 'Dispatch' 를 통해 전달된 'Action'을 'Reducer' 가 받아 'Store' 에 있는 state를 변경시킬때 마다 'Subscribe' 은 변화를 감지함
- 예제 소스
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { increase, decrease, login, logout } from 'store/actions/counter'
import store from 'store'
console.log(store.getState());
// 상태가 바뀔때마다 기록
let unsubscribe = store.subscribe(() =>
console.log(store.getState())
);
// 액션들을 보냄
store.dispatch(increase());
store.dispatch(decrease());
store.dispatch(login());
store.dispatch(logout());
// 상태 변경을 더 이상 받아보지 않음
unsubscribe();
class App extends Component {
render() {
return (
<div> </div>
)
}
}
export default App;