본문 바로가기

FrontEnd

리액트 디자인패턴 : State Reducer Pattern (스테이트 리듀서 패턴)

반응형

IOC : 제어의 역전

https://itchallenger.tistory.com/261?category=1063253 

 

제어의 역전(IOC : Inversion of Control) in React

원문 보기 사용 중인 루틴에 기능을 추가해달라는 요청을 받게 되면? 리액트 컴포넌트는 props를 추가하고, 리액트 훅은 argument를 추가할 것이다. 구현 로직은 점점 복잡해진다. 이같은 조치는 다

itchallenger.tistory.com

자바는 익명 클래스의 메서드를 오버라이딩하여 파라미터로 넘기는 코딩이 있다. (요즘은 람다-문법적 설탕임.)

이는 특정 객체의 특정 메서드를 호출한다.

이는 js에서 콜백을 넘기는 것과 동일(isomorphic)하다.

제어의 역전이란 내 코드의 실행을 다른 API에 위임하는 것이다.

DI(의존성 주입)은 제어의 역전을 위해 내 코드조각을 집어넣는 것이다.

State Reducer Pattern

개발자가 default reducer의 특정 action type에 대한 동작을 오버라이딩 할 수 있도록 하는 패턴이다.

The State Reducer Pattern with React Hooks

 

The State Reducer Pattern with React Hooks

A pattern for you to use in custom hooks to enhance the power and flexibility of your hooks.

kentcdodds.com

How

1. 사용자에게 State의 타입, Action의 타입을 공개한다.

Action 타입과 리듀서 구현은 긴밀하게 연결되어있기 때문에, 허용하는 Action의 타입과 State의 타입을 제공할 필요가 있다.

export type ToggleAction =
  | { type: "toggle" }
  | { type: "reset"; initialState: ToggleState };
  
  
export interface ToggleState {
  on: boolean;
};

또한 default 리듀서도 정의한다.

State Reducer의 시그니처는 관례에 따라 (prev:State,action:Action)=>(next:State)로 고정한다.

function toggleReducer(state: ToggleState, action: ToggleAction): ToggleState {
  switch (action.type) {
    case actionTypes.toggle: {
      return { on: !state.on };
    }
    case actionTypes.reset: {
      return action.initialState;
    }
    default: {
      throw new Error(`Unsupported type`);
    }
  }
}

 

 

2. 개발자는 reducer를 정의하여 사용한다.

원래 reducer를 action type에 따라 개발자 입맛대로 오버라이딩하여 사용한다.

이때, 원래 reducer의 동작을 망치지 않도록 주의해서 사용해야 한다.

즉, 동적 타이핑 언어 입장에선, 내부 구현을 알아야 하는 내용 결합도가 발생한다.

타입스크립트라면 정해진 인터페이스 대로 reducer를 동작하도록 정의하면 되는 것이라 별 문제가 없을 듯 하다.

// ... import
const toggleStateReducer = (clickedTooMuch: boolean) =>
  function toggleStateReducer(state: ToggleState, action: ToggleAction):ToggleState {
    if (action.type === "toggle" && clickedTooMuch) {
      return { on: state.on };
    }
    return toggleReducer(state, action);
  };

function App() {
  const [timesClicked, setTimesClicked] = React.useState(0);
  const clickedTooMuch = timesClicked >= 4;
  const { on, getTogglerProps, getResetterProps } = useToggle({
    reducer: toggleStateReducer(clickedTooMuch)
  });

  return (
    <div className="App">
      <div className="App-header ">
        <Switch
          {...getTogglerProps({
            disabled: clickedTooMuch,
            on: on,
            onClick: () => setTimesClicked((count) => count + 1)
          })}
        />
        {clickedTooMuch ? (
          <div data-testid="notice">
            Whoa, you clicked too much!
            <br />
          </div>
        ) : timesClicked > 0 ? (
          <div data-testid="click-count">Click count: {timesClicked}</div>
        ) : null}
        <button {...getResetterProps({ onClick: () => setTimesClicked(0) })}>
          Reset!!!
        </button>
      </div>
    </div>
  );
}

export default App;

전체 코드는 아래에서 확인할 수 있다.

결론 :

state reducer pattern은 reducer를 오버라이딩 할 수 있도록 해준다.

반응형