React 기초

[React] State와 생명주기 (4)

Evolving Developer 2023. 2. 7. 22:15

시계 예제

const root = ReactDOM.createRoot(document.getElementById('root'));

function Clock(props) {
  return (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {props.date.toLocaleTimeString()}.</h2>
    </div>
  );
}

function tick() {
  root.render(<Clock date={new Date()} />);
}

setInterval(tick, 1000);

- 1초마다 <Clock/>을 새롭게 렌더링하는 방법

   -> <Clock/>이 스스로 업데이트하도록 만들고 싶다면?


함수에서 클래스로 변환하기

1. React.Component를 확장하는 동일한 이름의 class를 생성

class Clock extends React.Component

2. render()라고 불리는 빈 메서드 추가

- 업데이트가 발생할 때마다 호출됨

class Clock extends React.Component {
	render() {
    }
 }

3. 함수의 내용을 render() 메서드 안으로 옮김

4. render() 내용 안에 있는 props를 this.props로 변경

class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

props에서 state로 변경하기 

1. render() 메서드 안에 this.props.date -> this.state.date로 변경

class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

2. 초기 this.state를 지정하는 class constructor 추가

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

- 클래스 컴포넌트는 항상 props로 기본 constructor를 호출

3. <Clock /> 요소에서 date 관련 코드 삭제

root.render(<Clock />);

전체 코드

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()}; // state 변수에 현재 시간을 담음 (고정값)
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Clock />);

- 순간의 시간에 멈춰있는 결과가 출력됨 ex) It is 오후 8:51:40.

  -> 초마다 업데이트 되게 하고 싶다면?


생명주기 메서드를 클래스에 추가하기

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
  }

  componentWillUnmount() {
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

1. componentDidMount()

  componentDidMount() {
    this.timerID = setInterval( // timerID라는 새 변수 추가
      () => this.tick(), // 초마다 tick() 함수 실행
      1000
    );
  }

2. componentWillUnmount()

componentWillUnmount() {
    clearInterval(this.timerID);
  }

3. tick()

  tick() {
    this.setState({ // state 변수의 값을 세팅
      date: new Date()
    });
  }

전체 코드

class Clock extends React.Component {
  constructor(props) {
    super(props);
    // 2. 현재 시간을 보여줘야 하기 때문에 현재 시각이 포함된 객체로 state 초기화
    this.state = {date: new Date()};
  }
  // 4. 생명 주기 메서드 호출
  componentDidMount() {
    this.timerID = setInterval( // 매초 tick() 메서드 호출
      () => this.tick(),
      1000
    );
  }
  
  // 6. Clock 컴포넌트가 DOM으로부터 한 번이라도 삭제된 적이 있다면 React는 타이머를 멈춤
  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  // 5. tick() 메서드 호출
  tick() {
    this.setState({ // state 값을 현재 시각으로 업데이트 -> 다시 rendering
      date: new Date()
    });
  }

// 3. render() 함수 호출 & state 내 date에 접근
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }

// 1. <Clock />이 root.render()로 전달될 때, Clock 컴포넌트의 constructor 호출
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Clock />);

State를 올바르게 사용하기

1. state를 직접 수정하지 않는다. 

- 대신 setState()를 사용

- constructor는 this.state를 지정할 수 있는 유일한 공간

// Wrong
this.state.comment = 'Hello';

// Correct
this.setState({comment: 'Hello'});

2. state는 비동기적

- React는 성능을 위해 여러 setState() 호출을 단일 업데이트로 한꺼번에 처리할 수 있음

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});

- 객체보다는 함수를 인자를 사용하자

// Correct
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

 

3. state 업데이트는 병합됨

- 변수 독립적으로 업데이트 가능

  constructor(props) {
    super(props);
    this.state = {
      posts: [], // 변수 1
      comments: [] // 변수 2
    };
  }

- posts 변수와 comments 변수를 갖는 state 객체

componentDidMount() {
    fetchPosts().then(response => {
      this.setState({
        posts: response.posts // 변수 1의 값 업데이트
      });
    });

    fetchComments().then(response => {
      this.setState({
        comments: response.comments // 변수 2의 값 업데이트
      });
    });
  }

- 독립적으로 업데이트 했으나 결국 한 state로 병합됨