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
}
  
*/