MobX Best Practices

김지태 a.k.a Originerd

Overview

useStrict

  • 최상위에서 한 번 `useStrict(true);` 실행 시 활성화
  • observable 값을 변경시킬 때 `action`을 사용하도록 강제
  • 무분별한 값 변경 방지
  • action은 트랜잭션을 만들어 주기 때문에 성능 개선 가능

Provider & inject

  • store를 singleton 으로 설정 시
    • 테스트에 보다 많은 mocking이 필요
    • 각 컴포넌트에서 필요 이상의 데이터를 다룰 수 있음
  • Provider / inject 사용 시
    • Provider에 store를 넣어주기 전 store 객체 생성
    • 테스트 시에도 각 테스트 별 필요할 때마다 store 생성 가능
    • inject를 이용해 각 컴포넌트에 꼭 필요한 데이터만 전달 가능
import { Provider } from 'mobx-react';
import * as React from 'react';

import BoardContainer from '../game/BoardContainer';
import stores from '../stores';

const App = () => (
  <Provider {...stores}>
    <BoardContainer />
  </Provider>
);

export default App;
import { inject, observer } from 'mobx-react';

import { Stores } from '../stores';
import Board from './Board';

export default inject((stores: Stores) => ({
  board: stores.gameStore.board,
}))(observer(Board));

Provider

inject

Container & Presentational components

  • Redux Best Practices 중 하나
  • Container components
    • 일반적으로 inject로 higher order component 구성
    • 필요한 데이터를 store에서 가져와 정제
    • store의 값을 변경하는 로직 등에 대해서도 관여
  • Presentational components
    • 어떻게 보여질지 결정
    • 전달받은 props 및 필요하다면 state를 사용해 데이터 출력
import { inject, observer } from 'mobx-react';

import { Stores } from '../stores';
import Board from './Board';

export default inject((stores: Stores) => ({
  board: stores.gameStore.board,
}))(observer(Board));
import * as React from 'react';
import { Segment } from 'semantic-ui-react';

import Row from './Row';
import { StoneType } from './GameStore';

const styles = {
  rowsContainer: {
    display: 'flex',
    flex: 1,
    flexDirection: 'column' as 'column',
    height: '100%',
    width: '100%',
  },
};

interface Props {
  board: StoneType[][];
}

const renderRows = (board: StoneType[][]) =>
  board.map((row, i) =>
    <Row columns={row} key={i} />
  );

const Board = ({ board }: Props) => (
  <Segment className="board">
    <div style={styles.rowsContainer}>
      {renderRows(board)}
    </div>
  </Segment>
);

export default Board;

Container component

Presentational component

Reactions

  • Reactions: autorun, autorunAsync, reaction, when
  • Reactions는 MobX의 핵심 기능
  • Observable 혹은 computed 값이 변경될 때마다 실행
  • 매우 강력하나, 자칫 프로그램의 흐름을 읽기 어려워지기 쉬움
    • 가능하면 Reaction 대신 명시적인 로직 처리를 권장
  • 디버깅 시 용의하도록 각 Reaction에 이름을 붙이길 권장
  • 각 Reaction 실행 시 dispose 할 수 있는 함수 반환
    • 효율성을 위해 필요가 없어진 경우 반드시 dispose 함수 실행
const number = observable(1);

const disposer = autorun(
  'trackNumberChange',
  () => {
    console.log(number.get()));
  },
);

number.set(1); // prints '1'
number.set(2); // prints '2'
number.set(3); // prints '3'

disposer();

numbers.set(4); // prints nothing

autorun

summary.

  • useStrict 활성화
  • singleton 대신 Provider & inject 사용
  • Container & Presentational components 적용
  • Reactions는 강력! 하지만,
    • 되도록 적게 사용
    • 사용 시에는 이름 붙이기
    • 사용 후에는 반드시 제거

Q & A

감사합니다

originerd@gmail.com

https://github.com/originerd

MobX Best Practices

By Woongjae Lee

MobX Best Practices

타입스크립트 한국 유저 그룹 리액트 스터디 201706

  • 1,363