redux 분석 - 데이터 흐름

박제영·2022년 8월 17일
0

오픈소스

목록 보기
2/2

아래의 소스는 Kevin Heis라는 외국분이 만든 미니 리덕스이며
내가 일부만 수정하고 리덕스의 데이터 흐름을 파악해 본 내용이다
출처 https://github.com/heiskr/prezzy-redux-scratch

  const count = function (state, action) {
    if (action.type === 'INCREMENT') {
      return state ? state + 1 : 1;
    }
  };

 const reducer = combineReducers({
    count: count,
  });

리듀서를 만들고 combineReducers에 전달

function combineReducers(reducers) {
  var keys = Object.keys(reducers); - 다른부분
  return function (state, action) { - 공통부분
    state = state || {};
    var next = {};
    let hasChanged = false;
    keys.forEach(function (key) {
      const preState = state[key];
      const nextState = reducers[key](preState, action);
      next[key] = nextState;
      hasChanged = hasChanged || nextState !== preState;
    });
    return hasChanged ? next : state;
  };
}

위 코드는 실제 리덕스 코드처럼 불변성 검사하도록 내가 변형했다
combineReducers는
리듀서끼리의
같은 기능을 하는 부분과 다른 값을 가지고 있는데
리듀서를 호출할 키값만 클로저로 보관하는 부분만 다르고
나머지는 공통이다

왜 combineReducers를 스토어를 호출하기 전에 한 번만 호출하는지를 알 수있다

  var store = createStore(reducer);

만들어진 리듀서를 스토어에 전달

function createStore(reducer) {
  let state = {number: 0, diff: 1};
  var listeners = [];

  function getState() {
    return state;
  }

  function subscribe(listener) {
    listeners.push(listener);
    return function unsubscribe() {
      listeners.splice(listeners.indexOf(listener), 1);
    };
  }

  function dispatch(action) {
    state = reducer(state, action);
    listeners.forEach(function (listener) {
      listener();
    });
    return action;
  }

  return {
    getState: getState,
    subscribe: subscribe,
    dispatch: dispatch,
  };
}

스토어를 보면 의문이 들수있다
왜 리듀서를 클로저로 만들어서 스토어에 넘길까?
그냥 해당하는 함수만 넘기면 되는게 아닐까?

그 이유는 state에 있는 것 같다
state 하나에서 여러 리듀서에서 관리하는 상태를 모두 관리하기 때문이다.

state : { -대충 이런 모양이었다
  counter: {count:1,diff:2},
  todos: {todo:"리덕스 분석", done:false}
}

한 가지 더 의문이 생긴다
dispatch 하게되면 값의 변경이 있던 없던
리스너를 전부 호출한다는 점이다

	try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }

    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }

위 코드는 실제 리덕스 코드다
미니 리덕스와 마찬가지로 값의 변경 여부에 관계없이
해당 리듀서의 모든 리스너를 호출한다

성능저하로 이어질 수 있는 부분이라고 생각한다
근데 내가 분석한거라서 틀릴 수가 있다
일단 나로서는 이 부분이 성능저하로 이어지는 부분이라고 파악이 된다

대략적인 흐름의 설명은 끝이 났다

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
bindActionCreators에 대한 코드만 마저 해석해보고
미들웨어쪽은 굳이 손대지 않겟다

  var actionCreators = {
        incrementCount: function() {
            return {type: 'INCREMENT'};
        }
    }
  var bounded = bindActionCreators(actionCreators, store.dispatch);

전달 인자는 위와 같고

function bindActionCreators(actionCreators, dispatch) {
  var bounded = {};
  Object.keys(actionCreators).forEach(function (key) {
    var actionCreator = actionCreators[key];
    bounded[key] = function () {
      var args = Array.prototype.slice.call(arguments);
      dispatch(actionCreator.apply(null, args));
    };
  });
  return bounded;
}

함수 구현은 위와 같다

 Object.keys(actionCreators).forEach(function (key) {
    var actionCreator = actionCreators[key];

여기만 살펴봐도 어떤 객체에 어떤 키값이 담겨있던 간에
해당 키값으로 해당 함수를 꺼내서 사용할 수있게금 작성되어있다
(프로퍼티 내용이 함수라는 가정하에 이부분은 현재 js니까 나중에 ts로 강제할수잇다)
굉장히 유연하다고 느껴진다

전달된 dispatch는 클로저로 보관하여
return된 객체에서 사용할 수 있도록 참조되어 있다

    document.querySelector('#example-3 button')
        .addEventListener('click', function() {
            bounded.incrementCount();
        });

사용은 위와같이 한다

이제 왜 클로저, 실행컨텍스트 이러한 기본적인 js가 중요한지 와닿기 시작한다
이번 리덕스를 분석하며 정말 즐겁고 얻어가는 것도 있던 시간이었다.

profile
개발 도중 만난 문제 해결을 서술하거나 기록 및 개인의 생각을 정리한 블로그

0개의 댓글