Programming/React
리덕스] 개념 이해
고양이의시간
2020. 2. 23. 11:23
리덕스
상태 관리 로직을 컴포넌트 밖에서 처리 함으로써, state 를 보다 효율적으로 관리 하기위한 라이브러리.
store 라는 객체 내부에 상태를 담고, 액션이 디스패치 되었을때 reducer 함수를 이용하여 상태를 변화시킨다.
리액트와 리덕스는 별개로 동작하므로, 함께 사용이 가능하다.
리덕스는 편리하기는 하나 모든 state 를 리덕스로 처리하게 되면
상태값, 액션, 리듀서를 모두 정의해야 하므로 귀찮아지는 단점이 있다.
그래서, 서버 통신이 필요하는 것과 같은 유형의 작업을 주로 리덕스로 구현하고
다른 자잘한 state 는 리액트로 사용하는 것이 좋다.
리덕스의 한계
액션을 실행하면 바로 실행한다.(동기)
특정시간이나 특정동작 이후에 액션을 끼워넣을 수 없다.(예. 로그인같은 서버와 통신이 필요할때)
그래서 비동기 작업을 위해 사용하는 것이 리덕스 사가
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.6.0/redux.js"></script>
/***************************************************************************************
리덕스 규칙
1) store 는 단 한개여야만 한다.
2) 리덕스 상태값인 state는 읽을수만 있으며, 직접 수정은 되지 않는다.
상태를 업데이트 하려면, 새 상태 객체를 만들어 넣어줘야 한다.
- 직접 수정시, 리덕스의 구독함수를 제대로 실행하지 못하게 된다.
3) 모든 변화는 순수 함수로 구성해야 한다.(리듀서 함수)
- 순수 함수: 결과값을 출력할때는, 파라미터 값에만 의존해야 하며, 같은 파라미터는 언제나 같은 결과를 출력
예) 외부 네트워크& DB 직접 접근, new Date(), Math.random() 는 순수함수에서 사용할 수 없다
***************************************************************************************/
/***************************************************************************************
액션: 상태변화를 일으킬때 참조하는 객체
예) 가입버튼을 눌렀다.
***************************************************************************************/
// 1. 액션 타입을 상수값으로 정의
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
// 2. 액션 생성함수 정의
const increment = (diff) => ({
type: INCREMENT,
diff: diff,
});
const decrement = (diff) => ({
type: DECREMENT,
diff: diff,
});
// console.log(increment(1));
/*
결과:
[object Object] {
diff: 1,
type: "INCREMENT"
}
*/
/***************************************************************************************
리듀서: 상태에 변화를 일으키는 함수
파라미터1 : 현재상태/ 파라미터2: 액션객체
액션 타입에 따라 현재 상태와 액션 객체를 참조하여 새 객체를 만들어 준다.
리덕스에서 상태를 업데이트할때, 값의 직접 수정은 불가. 새로운 객체를 만들고 그안에 상태를 정의해야 한다.
***************************************************************************************/
// 1. 리듀서가 초기에 사용할 초기상태값
const initialState = {
number: 1,
foo: 'bar',
baz: 'qux',
};
// 2. 리듀서 함수 정의
function counter(state=initialState, action){
// console.log(111);
switch(action.type){
case INCREMENT:
return {
...state, // 펼침 연산자
number: state.number + action.diff,
};
// 펼침 연산자 사용했을때와 같은 결과
// return Object.assign({}, state, {
// number: state.number + action.diff
// });
// return { number: state.number + action.diff };
case DECREMENT:
return Object.assign({}, state, {
number: state.number - action.diff
});
default:
return state;
};
}
// 3.리듀스 함수 실행 - 이렇게 직접 호출하지는 않음
// console.log(counter(undefined, increment(1)));
/*
결과
[object Object] {
baz: "qux",
foo: "bar",
number: 2
}
*/
/***************************************************************************************
리덕스 스토어
***************************************************************************************/
// 1. 리덕스 스토어 import
const { createStore } = Redux;
// 2. 스토어 생성 - 리듀서함수를 파라미터로 사용
const store = createStore(counter);
/***************************************************************************************
컴포넌트에서 리덕스 스토어 구독
subscribe() : 구독 / unsubscribe() : 구독 취소
- 실제 프로젝트에서는 react-redux의 connect()함수가 대신함
***************************************************************************************/
// 함수형태의 파라미터를 받고, 스토어 상태가 변화할때마다 호출함
// getState() : 현재 스토어 상태 반환
const unsubscribe = store.subscribe(() => {
console.log(store.getState());
});
/***************************************************************************************
dispatch: 액션을 스토어에 전달
- 액션들이 디스패치 될때마다 구독했던 함수를 실행
***************************************************************************************/
store.dispatch(increment(5));
store.dispatch(decrement(3));
/*
결과
[object Object] {
baz: "qux",
foo: "bar",
number: 6
}
[object Object] {
baz: "qux",
foo: "bar",
number: 3
}
*/