Redux - 21.05.17
Redux : 컴포넌트의 state를 관리하기 위해 필요. 다양한 프레임 워크(React, Angular, Vue JS 등)에서 사용 가능.
- Redux 설치 방법
$ npm i redux --dev
Redux가 필요한 이유 : React(+Hooks)만 사용하여 state 관리 시 데이터의 흐름은 단방향이기 때문에 중첩 깊이가 깊어질 수록 중복 코드가 많아지고 코드가 직관적이지 않게 된다. 그러므로 Resux를 사용하여 state를 별도로 관리함으로써 이러한 복잡성을 줄여 준다.
Redux의 3가지 원칙
- 전체 애플리케이션의 state는 단일 store에 저장
- state는 읽기 전용 : 상태를 변경하는 유일한 방법은 무슨 일이 일어 났는지 설명하는 객체(object)인 Action을 보내는 것
- 상태 변경은 순수 함수로 작성 : 상태 트리가 Action에 의해 어떻게 변환되는지 지정하려면 순수 reducer 함수를 작성
*순수 함수 : 주어진 input에 대해 정확히 같은 output을 리턴하는걸 의미 (input, output 과정 중 해당 함수의 외부 영향 없는 경우)
Redux 주요 개념
Store :
Redux의 핵심으로 Redux의 모든 부분을 총괄
store 안에서 어플리케이션의 state는 하나의 불변의(immutable) 객체(object)로 존재.
store는 action을 받는 즉시 reducer를 작동 시킨다.
reducer는 state를 return
createStore : store 생성 함수. 첫번째 인자로 reducer를 갖는다.
// store/index.js
import { createStore } from 'redux';
import rootReducer from '../reducers/index';
// createStore : store 생성 함수. 첫번째 인자로 reducer를 갖는다.
const store = createStore(rootReducer)
export default store;
reducer : state를 생산 (내가 직접 만든 state가 아님)
reducer는 state와 action 2개의 파라미터를 가진 자바스크립트 함수
reducer 함수는 스위치 구문을 갖는다.(if 문도 상관없음)
reducer는 action type에 따라서 새로운 state를 계산하고, 일치하는 action type이 없을 때는 적어도 초기 state를 리턴 할 것이다.
store는 action을 받는 즉시 reducer를 작동 시킨다.
reducer는 state를 return
reducer : 2개의 파라미터(state와 action)를 받는 순수 자바스크립트 함수. React에서 state는 setState로 변경하지만, Redux에서는 그렇게 할 수가 없다. Redux에서 state는 reducer를 거쳐 return한다.
// js/reducers.js
const initialState = {
data: [];
};
// 순수 reducer 함수. 첫번째 인자로 initialState, 두번째 인자로 action
const rootReducer = (state = initialState, action) => state;
// 위 함수는 아무것도 하지 않고 initial state를 단순히 리턴 할 뿐.
// action을 추가해야한다 !
export default rootReducer;
action
redux에서 state를 바꾸는 유일한 방법은 store에 action를 보내야한다. 이를 ‘action을 dispatch한다’라고 표현한다.
immutable(불변) state를 바꾸는 방법은 state를 복사 후 새로운 데이터를 추가하여 새로운 state를 만들면 기존의 state에는 영향이 없다. 알아두어야 할 점은 action은 단지 자바스크립트 객체일 뿐이라는 것이다.
{
// 모든 action들은 state가 어떻게 변해야 하는지를 나타내는 type 속성 필요
type: 'ADD_DATA',
// reducer는 나중에 이 payload를 현재의 state에 추가한다.
payload: {
name: 'React Redux Tutorial',
id: 1
}
}
// action은 단지 자바스크립트 객체일 뿐
action creator : 모든 action을 함수로 감싸는 것은 최선의 방법이며 이러한 함수를 action creator라고 한다.
// actions/index.js
// 타입 불러오기
import { ADD_DATA } from "../constants/action-types";
// action creator : action들을 감싸는 함수
export const addData = data => ({
// type 속성은 단순히 문자열(string)일 뿐이다.
// reducer는 state를 어떻게 처리해야 하는지를 판단하기 위해 이 문자열을 사용
// 문자열은 오타가 나거나 중복되기 쉬운 경향이 있기 때문에 action의 type을 상수로 선언하는 편이 좋다.
// 상수로 선언하면 추후 디버깅 시 어려운 오류를 피하는데 좋다.
type: "ADD_DATA",
payload: data
});
// constants/action-types.js
export const ADD_DATA = "ADD_DATA";
// type 속성은 단순히 문자열(string)일 뿐이다.
// reducer는 state를 어떻게 처리해야 하는지를 판단하기 위해 이 문자열을 사용
// 문자열은 오타가 나거나 중복되기 쉬운 경향이 있기 때문에 action의 type을 상수로 선언하는 편이 좋다.
// 상수로 선언하면 추후 디버깅 시 어려운 오류를 피하는데 좋다.
- Reducer에 action 추가
// reducers/index.js
// action type 불러오기
import { ADD_DATA } from "..//constants/action-types";
const initialState = {
data = [];
}
// reducer에 action 추가
// reducer는 action type에 따라서 새로운 state를 계산하고, 일치하는 action type이 없을 때는 적어도 초기 state를 리턴
const rootReducer = (state = initialState, action) {
// action type의 값을 기준으로 switch
// switch 문을 if 문으로 변경 가능
switch (action.type) {
// action type이 ADD_DATA인 경우
case ADD_DATA:
// state는 불변해야하므로 spread operator를 사용
// (push 등 원본 배열을 바꾸는 method는 사용하면 안된다.)
// state인 data의 원본을 복사하여 payload를 넣어준다.
return { ...state, data: [...state.data, action.payload] } ;
// 위와 같이 작성하면 초기 state는 전혀 영향을 받지 않고 state 객체는 변경되지 않는다.
// return 값은 초기 state의 복사본이다.
// cast에 해당 되지 않는 경우 기본 값
default:
// 일치하는 action type이 없을 때는 적어도 초기 state를 리턴
return state;
}
};
export default rootReducer;
Redux Store Method
- test code
// src/App.js
import React from 'react';
import './App.css';
import store from "./js/store/index";
import { addArticle } from "./js/actions/index";
// 콘솔에서 테스트해보기 위해 작성한 코드
window.store = store;
window.addArticle = addArticle;
class App extends React.Component {
render() {
return (
<div>
</div>
);
}
}
export default App;
- getState() : 현재 state를 return
// syntax
// store에는 초기 state가 있다.
store.getState()
store.getState() // {data: Array(0)}
- subscribe() : state 변화 감지
subscribe은 action이 보내(dispatch)질 때마다 콜백 함수가 호출 된다. action을 dispatch한다는 것은 state를 바꾸기 위해 store에 알리는 것을 의미한다.
// syntax
store.subscribe(콜백 할 함수)
store.subscribe(() => console.log('Hello Redex')) // action이 dispatch 될 때마다 콘솔에 Hello Redux 출력
- dispatch() : state에 action 보내기(dispatch)
state를 바꾸기 위해 action을 dispatch 할 필요가 있다. action을 dispatch 하기위해서는 dispatch()를 호출하면 된다.
// state에 action 추가
store.dispatch( addData({ title: 'React Redux Tutorial', id: 1 }) )
// state 변경 확인
store.getState() // {articles: Array(1)}
Action, Reducer, Dispatch, Store를 연결 해주기 위한 방법
- react-redux 설치
$ npm i react-redux
- connect parameter를 통해 mapStateToProps, mapDispatchToProps 등 의 메소드를 이용
import React from "react";
import { connect } from 'react-redux';
import { incrementAction, decrementAction } from "./Actions";
const counterSelector = state => state.counter;
const CounterComponents = props => {
return (
<>
<p> count: {props.counter} </p>
<button onClick={props.increment}>increment</button>
<button onClick={props.decrement}>decrement</button>
</>
);
};
const mapStateToProps = state => ({ counter: counterSelector(state) });
const mapDispatchToProps = dispatch => (
{ increment: () => dispatch(incrementAction()),
decrement: () => dispatch(decrementAction()) }
);
const Counter = connect( mapStateToProps, mapDispatchToProps )(CounterComponents);
- Redux hooks : useSelector(), useDispatch() 등 (Redux hooks가 보다 최근에 나온 방법이고 비교적 사용하기 쉬움)
useSelector() : 컴포넌트와 state를 연결하는 역할. 컴포넌트에서 useSelector 메소드를 통해 store의 state에 접근
useDispatch() : Action 객체를 Reducer로 전달해주는 메소드. 컴포넌트에서 사용.
// 컴포넌트
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { incrementAction, decrementAction } from "./Actions";
const counterSelector = state => state.counter;
const Counter = props => {
// useDispatch() : Action 객체를 Reducer로 전달해주는 메소드.
const dispatch = useDispatch();
// useSelector(state) : 컴포넌트와 state를 연결.
const counter = useSelector(counterSelector);
return (
<>
<p> count: {counter} </p>
<button onClick={dispatch(incrementAction())}>increment</button>
<button onClick={dispatch(decrementAction())}>decrement</button>
</>
);
};
참고 : https://ko.redux.js.org/ https://slee2540.tistory.com/38 https://usonkrap.github.io/2018/12/15/React-Redux-Tutorial-for-Beginners.html