React.js - state

gyumin_2·2022년 2월 14일
0

React.js

목록 보기
5/13
post-thumbnail

상태(state)

  • 컴포넌트에 저장하여 사용(변경)가능한 데이터를 상태(state)라고 한다.
  • props는 부모로 부터 전달받은 데이터이며 변경이 불가능한 읽기전용 데이터인 반면, state는 캡슐화 되어있어 비공개이며 컴포넌트에 의해 제어(변경)이 가능하다.

클래스 컴포넌트에서 state 사용하기

state 생성하기

  1. 클래스 내부에서 생성자를 만든다.
    • state는 컴포넌트가 만들어질 때 가장 먼저 설정되기 때문에 constructor 안에 작성한다.
    • 생성자 함수의 매개변수에 props를 할당한다.
    • 클래스는 리액트를 상속받아 작성하므로 super() 함수를 호출해야하며, super() 함수의 매개변수로 props를 할당한다.
  2. 생성자 내부에서 state 객체를 만들고, 객체에 원하는 데이터의 초기값을 설정한다.
  3. render() 함수 내부에서 this.state.[상태] 형식으로 state 객체에 접근한다.
class User extends React.Component {
    constructor(props) { // props를 매개변수로 생성자를 호출
        super(props);
				// 생성자 내부에 state 객체 생성 
        this.state = {
            name: 'Kim',
            age: 22,
            number: '010-1111-2222'
        };
    }

    render() {
        return ( // state 조회하기
            <div>
                <h1>Hello World</h1>
                <p>{this.state.name}</p>
                <p>{this.state.age + 1}</p>
                <p>{this.state.number}</p>
            </div>
        )
    }
}

ReactDOM.render(
    <User/>,
    document.getElementById('app')
);

state를 constructor에서 꺼내기

  • state의 초깃값을 지정하기 위해서는 constructor 메서드를 선언하는 방법 말고 다른 방법으로도 초깃값을 설정할 수 있다.
class User extends React.Component {
    state = {
        name: 'Kim',
        age: 22,
        number: '010-1111-2222'
    };

    render() {
				// 비구조화 할당 사용
				const { name, age, number } = this.state;
        return (
            <div>
                <h1>Hello World</h1>
                <p>{name}</p>
                <p>{age + 1}</p>
                <p>{number}</p>
            </div>
        )
    }
}

ReactDOM.render(
    <User/>,
    document.getElementById('app')
);

state 사용하기

setState()

  • 컴포넌트에서 state값을 변경할 때는 반드시 setState() 메소드를 사용하여 변경해야 한다.
  • setState(updater[, callback])
    • 변경할 state값을 전달받아 기존 state를 변경하는 기능의 함수
    • 현재 state를 덮어씌우는 것이 아닌 merge하는 동작을 한다. 이는 state역시 object에 여러 키로 이루어져 있고, update하지 않는 key, value는 그대로 유지해야 하기 때문이다.
    • updater에 올 수 있는 자료형은 Object와 Function이다.

setState()의 매개변수 updater가 Object인 경우

  • state를 변경하는 가장 기본적인 형태로, 변경할 state의 프로퍼티를 객체형식으로 작성하여 매개변수에 넣는다.
  • 정상적으로 state가 변경되고 나면, component life cycle 로직(shouldComponentUpdate())을 타게 된다.
this.setState({myKey: 'my new value'});

setState()의 매개변수 updater가 Function인 경우

  • 보통 이전 state값을 이용하면서 state값을 update할때 사용된다. updater가 Object인 경우 이전 state값을 그냥 merge해버리는 반면, updater가 Function인 경우에는 이전 state 값을 사용할 수 있기 때문에 명확하게, 이전값을 사용하거나, 이전값 비교등이 필요할때 사용하면 좋다.
  • updater가 Function인 경우 함수는 이전 state(prevState)와 현재 state(props)를 매개변수로 갖는다.
this.setState((prevState, props) => {
  return {myInteger: prevState.myInteger + props.step};
});

setState()가 callback을 매개변수로 갖는 경우

  • setState()의 두번 째 매개변수인 callback은 optional이며, updater의 자료형과 상관없이 사용 가능하다.
    // when updater is Object
    this.setState({myKey: 'my new value'}, () => {
      console.log('setState complete');
    });
    
    // when updater is Function
    this.setState((prevState, props) => {
      return {myInteger: prevState.myInteger + props.step};
    }, () => {
      console.log('setState complete');
    });
  • callback은 setState() 함수로 state 변경이 완료되고 component가 re-rendered된 이후 호출된다. 따라서, re-rendered이후의 어떤 확인이나 동작이 필요할때 간단히 기술해서 사용 가능하다.
  • 하지만 공식 문서에는, state변경 후 callback에서 어떤 동작을 하기보다는, componentDidUpdate()을 사용하라고 추천하고 있다.

state 사용시 주의 사항

  1. state는 반드시 setState() 함수를 이용해서 변경해야 한다.

    • state를 직접 변경할 경우 리액트는 이를 감지하지 못하기 때문에 반드시 setState()함수를 이용하여 변경해야 한다.
    // state를 직접 변경하는 경우 - react가 state 변경을 감지하지 못한다.
    this.state.comment = 'Hello';
    
    // setState()로 state를 변경한 경우 - react가 감지
    this.setState({comment: 'Hello'});
  2. setState()는 state를 변경(update)하는 것이 아닌 병합(merge)한다.

    • setState()를 호출 할 때, 리액트는 updater에 주어진 객체를 현재 state로 병합한다.
    • 병합은 얇게 이루어지기 때문에 아래처럼 state의 프로퍼티를 독립적으로 업데이트 할 수 있으며, 한 프로퍼티를 업데이트 할 경우 다른 프로퍼티에게 영향을 미치지 않는다.
      constructor(props) {
        super(props);
        this.state = {
          posts: [],
          comments: []
        };
      }
      
      componentDidMount() {
        fetchPosts().then(response => {
          this.setState({ // posts를 업데이트 하는 거기 때문에 comment에 영향X
            posts: response.posts
          });
        });
      
        fetchComments().then(response => {
          this.setState({
            comments: response.comments
          });
        });
      }
  3. setState()는 비동기로 작동한다.

    • 리액트는 성능을 위해 setState()을 여러번 호출 할 경우 이를 단일 업데이트로 한꺼번에 처리한다. 즉 setState()는 즉시 값을 merge하지 않고, pending된 형태로 동작한다. 따라서 setState()호출후 this.state로 값을 확인했을때, 값이 변경되어 있을 수도 있고, 안되어 있을 수도 있다.
    • this.propsthis.state가 비동기적으로 업데이트될 수 있기 때문에 state변경이 이전 state에 의존적인 경우 업데이트가 제대로 이루어지지 않을 수 있다. 때문에 이 경우에는 매개변수가 Function인 setState() 함수를 사용하여 이전 값을 참조하여 state를 업데이트 하는 것이 좋다.
      // state 변경이 이전 state나 props를 참조할 경우
      
      // 이 방식은 업데이트가 제대로 이루이지지 않을 수 있다.
      this.setState({
        counter: this.state.counter + this.props.increment,
      });
      
      // 이 경우에는 Function을 매개변수로 갖는 방식으로 state를 변경해야 한다.
      this.setState((state, props) => ({
        counter: state.counter + props.increment
      }));
    • 리액트는 여러번의 serStae()를 단일 업데이트로 처리하기 때문에 모든 state가 변경된 후 화면이 리랜더링 된다.
  4. setState()을 호출하고나면, component life cycle 함수인 shouldComponentUpdate()가 호출된다. setState()을 호출했다고 해서, 항상 re-rendering 되는 것은 아니다. 즉 shouldComponentUpdate()의 리턴값에 따라 re-rendering 여부가 결정된다.

setState()호출 부터 렌더링까지 흐름

  • setState() 실행 → state 병합 → render() 호출 → callback 실행

forceUpdate()

component.forceUpdate(callback)
  • 기본적으로, state나 props가 변경되면 컴포넌트를 리랜더링 된다. 하지만 state나 props가 아닌, 다른 data을 통해서, render() 함수가 동작 되어야 하는 경우가 있을 수 있다. 이런 경우, React의 기본적인 component life cycle와 무관하기 때문에, 직접 React에게 해당 component가 re-rendering되야 한다고 알려야 한다. 이때 사용가능한 함수가 forceUpdate() 이다.
  • forceUpdate()을 호출하면 곧바로 render()가 호출된다. 즉 shouldComponentUpdate()는 건너뛴다. 해당 메소드를 건너 뛰더라도, child components의 life cycle method는 정상적으로 다 호출된다. (즉 child component의 shouldComponentUpdate()는 호출된다.), 그렇다 하더라도, React는 DOM의 변경을 잘 찾아서 변경된 부분만 update해준다.
  • 리액트 공식 문서에서는 forceUpdate()의 사용을 최대한 피하고, this.props 와 this.state로 처리하도록 권고 하고 있다.

함수형 컴포넌트에서 state 사용 방법은 Hook 사용 편에서 자세히 설명하겠습니다.

profile
애기 프론트 엔드 개발자

0개의 댓글