React 컴포넌트 상태 객체

React 컴포넌트는 상태 값을 이용해 UI를 표현한다.
컴포넌트 상태는 객체의 인스턴스 속성(Properties)을 이용해 관리하며, 컴포넌트간에 단방향으로 데이터를 주고받는 props와 컴포넌트 내부에서 사용하는 state가 있다.

reactjs-props-state

props

props는 컴포넌트에서 사용할 데이터 중 변경되지 않는 데이터를 다룰때 사용한다. 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때 사용된다. props는 변경이 불가능하다.

class Parent extends React.Component {
  render() {
    let name = "mike";
    return (
       <h3> Hello <Child name={name} /></h3>
    );
  }
}

class Child extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    console.log(this.props); // Object {name: "mike"}
    return <a>{this.props.name}</a>;
  }
}

// default Props를 사용할 수 있다.
Child.defaultProps = {
  name: "unknown"
};

예제

state

컴포넌트에서 관리하는 상태 값으로 유동적인 데이터를 다룰 때, state 를 사용한다. state는 변경이 가능하고 변경할 때는 setState메서드를 사용해 상태를 변경한다. setState는 비동기로 동작하며 동작완료에 대한 콜백을 설정할 수 있다.

class Button extends React.Component {
  constructor() {
    super();
    this.state = {
      count: 0
    };
  }

  updateCount = () => {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <button onClick={this.updateCount}>
        Clicked {this.state.count} times
      </button>
    );
  }
}

예제

상태 저장 컴포넌트와 상태 비저장 컴포넌트 (Stateful x Stateless Component)

상태 저장 여부의 관점에서 상태저장 컴포넌트와 상태비저장 컴포넌트로 나눌수 있다.

상태 저장 컴포넌트

상태 저장 컴포넌트는 상태 객체와 라이프사이클을 가지는 컴포넌트다. 일반적으로 컴포넌트에 내부 state를 가지는 컴포넌트를 말한다.

stateful-vs-stateless-component-tutorial-component-with-state

class ButtonCounter extends React.Component {
  constructor() {
    super();
    this.state = { clicks: 0 };
  }

  handleClick = (e) => {
    this.setState({ clicks: this.state.clicks + 1});
  }

  render() {
    return (
      <Button onClick={this.handleClick} text={`You've clicked me ${this.state.clicks} times!`} />
    )
  }
}

상태 비저장 컴포넌트

상태 비저장 컴포넌트는 상채 객체가 없으며 컴포넌트 메서드 또는 다른 React의 라이프사이클 이벤트 또는 메서드를 갖지 않는다.
일반적으로 컴포넌트에 내부 state가 없으며, 함수로 구현되는 컴포넌트를 말한다.

stateful-vs-stateless-component-tutorial-component-with-prop

const Button = props => (<button onClick={props.onClick}>{props.text}</button>)

어떤걸 써야하나?

상태 비저장 컴포넌트를 사용하면 컴포넌트를 간결하게 작성할 수 있으며, 개별 상태가 없으므로 이해하기 쉽고 예측이 용이하여 테스트를 간단히 할 수 있다. 또한 라이프사이클을 사용하지 않으므로 불필요한 검사 및 메모리 할당을 줄일 수 있다. React의 바람직한 사례는 상태비저장 컴포넌트를 많이 사용하고 상태저장 컴포넌트는 적게 사용하는것이다.

React Stateless Functional Components

컨테이너 컴포넌트와 프레젠테이션 컴포넌트 (Container x Presentational Components)

이 패턴은 댄 아브라모프(Dan Abramov)가 Smart & Dumb 컴포넌트란 이름으로 사용하였다. 후에 더욱 정확한 의미 전달을 위해 명칭이 변경되었다. 컨테이너 컴포넌트와 프레젠테이션 컴포넌트는 로직 분리의 관점에서 구분하는 표현이다.
단순히 props, state로 화면을 그리는데 넘겨받는 것이 아니라 Ajax 요청이나 localStorage 등을 통해 데이터를 fetching 해야 할 경우 데이터까지 다루게 되므로 재사용성이 떨어진다. 이러한 상황에서 동작을 다루는 부분과 표현을 다루는 부분을 분리하여 작성하는 컴포넌트 패턴이다. state 존재 여부가 두 컴포넌트를 구분하는 것은 아니다

컨테이너 컴포넌트

  • JSX를 이용한 마크업이 거의 없다.
  • Ajax 요청, HOC 등을 이용해 render에 필요한 데이터를 Fetching 한다.
  • 데이터 Fetching 등을 위한 state가 존재할 수 있다.
class UserListContainer extends React.Component {
  constructor() {
    super();
    this.state = { users: [] }
  }

  componentDidMount() {
    fetchUsers(users => this.setState({ users }));
  }

  redner() {
    return <UserList users={this.state.users} />
  } 
}

프레젠테이션 컴포넌트

  • JSX를 이용한 마크업이 존재한다.
  • render에 필요한 데이터는 이미 존재한다고 가정한다.
  • UI를 위한 state가 존재할 수 있다.
const UserList = props => 
(<ul>
  {props.users.map(u => (
    <li>{u.name} - {u.age} years old</li>
  ))}
</ul>)

Pure Component

React에는 PureComponent가 존재하는데, Component와 유사하지만 라이프사이클의 shouldComponentUpdate에 얕은 비교(shallow comparison)를 하는 코드가 구현된 컴포넌트를 PureComponent라고 한다. React의 렌더링에서 shouldComponentUpdatetrue를 반환하면 재랜더링을 하는 구조인데, propsstate가 기존과 동일한 경우 불필요한 상황에서 재렌더링을 방지하여 성능을 향상시키려는 목적에서 만들어졌다.
PureComponent는 성능을 최적화하는데 활용되므로 성능상의 이슈에 맞닥뜨리지 않는 한 이 컴포넌트를 사용해야 하는지 고려할 필요는 없다.

  • PureComponent

    class Example extends PureComponent {
    // This won't re-render when the props DONT change
    render() {
      // ... do expensive compute on props ...
      return <SomeComponent someProp={props.someProp}/>
    }
    }
    
  • shouldComponentUpdate 내부 코드

    if (type.prototype && type.prototype.isPureReactComponent) {
      shouldUpdate = !shallowEqual(oldProps, props) || !shallowEqual(oldState, state);
    }
    

ReactShallowRenderer.js
shallowEqual.js

참고