20161105

React 15 Stateless Component 패턴

React 15 정착하면서 팀 룰로 정한 stateless functional component 패턴.

component 책임에 따라 두 종류로 나눔.

Stateless (Presentational) — props 받아서 JSX 내놓는 것만 하는 컴포넌트. 이벤트는 callback prop으로 위임. state 없음. 대부분은 이 형태로 충분.

const Badge = ({ text, onClick }) => (
  <span className="badge" onClick={onClick}>{text}</span>
);

Badge.propTypes = {
  text: PropTypes.string.isRequired,
  onClick: PropTypes.func
};

Container (Class) — state 관리, lifecycle 필요한 경우. 데이터 fetch, Redux 연결 등.

class UserList extends React.Component {
  state = { users: [], loading: true };
  componentDidMount() {
    fetch('/api/users')
      .then(r => r.json())
      .then(users => this.setState({ users, loading: false }));
  }
  render() {
    if (this.state.loading) return <Spinner/>;
    return (
      <ul>{this.state.users.map(u => <Badge key={u.id} text={u.name}/>)}</ul>
    );
  }
}

효과:

  • 컴포넌트 대부분이 stateless라 테스트가 엄청 쉬움. 그냥 props 넘기고 스냅샷 확인
  • 재사용성 높아짐 — 로직 없이 표현만 담당하니까 다른 화면에도 붙일 수 있음
  • 공식 문서에서도 "가능하면 functional로 써라" 라고 권장

주의: stateless component에는 아직 shouldComponentUpdate 같은 lifecycle 못 달아서 강제 memoize 필요하면 class로 돌아가야 함. React.PureComponent가 15부터 도입됐는데 이건 얕은 비교 기반이라 props에 객체가 깊게 있으면 안 먹음.

팀 컨벤션: 먼저 stateless로 작성 → 상태/사이드이펙트 필요해지면 class로 승격. 역방향 (class 먼저 작성 후 functional로 분해)은 하지 않도록. 이 습관이 리팩토링 시간 꽤 줄여줌.