React props와 state

Props와 State

  • 리액트 컴포넌트에서 다루는 데이터는 바로 props 와 state 두 개
  • props는 부모 컴포넌트가 자식 컴포넌트에게 주는 값
  • 자식 컴포넌트에서는 props 를 받아오기만하고, 받아온 props 를 직접 수정 할 수 는 없음
  • state 는 컴포넌트 내부에서 선언하며 내부에서 값을 변경 할 수 있음
  import React, { Component } from 'react';

  class MyName extends Component {
    render() {
      return (
        <div>
          안녕하세요!  이름은 <b>{this.props.name}</b> 입니다.
        </div>
      );
    }
  }

  export default MyName;
  • 자신이 받아온 props 값은 this. 키워드를 통하여 조회 할 수 있음. 지금 name 이라는 props 를 보여주도록 설정
  import React, { Component } from 'react';
  import MyName from './MyName';

  class App extends Component {
    render() {
      return (
        <MyName name="리액트" />
      );
    }
  }

  export default App;
  • import 를 통하여 컴포넌트를 불러오고, 렌더링
  • props 값은 name=”리액트” 이런식으로 태그의 속성을 설정해주는 것처럼 해줌

defaultProps

  • props 의 기본값을 설정
  import React, { Component } from 'react';

  class MyName extends Component {
    static defaultProps = {
      name: '기본이름'
    }
    render() {
      return (
        <div>
          안녕하세요!  이름은 <b>{this.props.name}</b> 입니다.
        </div>
      );
    }
  }

  export default MyName;
  • 태그에 name값을 생략해버리면 “기본이름”이 나타남.
  • 다음과 같은 형태로도 설정가능
  import React, { Component } from 'react';

  class MyName extends Component {
    render() {
      return (
        <div>
          안녕하세요!  이름은 <b>{this.props.name}</b> 입니다.
        </div>
      );
    }
  }

  MyName.defaultProps = {
    name: '기본이름'
  };

  export default MyName;

함수형 컴포넌트

  import React from 'react';

  const MyName = ({ name }) => {
    return (
      <div>
        안녕하세요!  이름은 {name} 입니다.
      </div>
    );
  };

  export default MyName;
  • 함수형 컴포넌트와 클래스형 컴포넌트의 주요 차이점은 state와 LifeCycle이 빠져있다는 점
  • 그래서 컴포넌트 초기 마운트가 아주 미세하게 빠르고 메모리 자원을 덜 사용함.

state

  • 동적인 데이터를 다룰 때 사용
  import React, { Component } from 'react';

  class Counter extends Component {
    state = {
      number: 0
    }

    handleIncrease = () => {
      this.setState({
        number: this.state.number + 1
      });
    }

    handleDecrease = () => {
      this.setState({
        number: this.state.number - 1
      });
    }

    render() {
      return (
        <div>
          <h1>카운터</h1>
          <div>: {this.state.number}</div>
          <button onClick={this.handleIncrease}>+</button>
          <button onClick={this.handleDecrease}>-</button>
        </div>
      );
    }
  }

  export default Counter;
  • 컴포넌트의 state를 정의할 때는 class fields 문법을 사용해서 정의
  • 만약 class field를 사용하지 않는다면 아래와 같이 사용
  import React, { Component } from 'react';

  class Counter extends Component {
    constructor(props) {
      super(props);
      this.state = {
        number: 0
      }
    }

    ...

  }
  • class fields 를 사용하는건 편의를 위함
  • constructor 에서 super(props) 를 호출 한 이유는, 우리가 컴포넌트를 만들게 되면서, Component 를 상속했으며, 우리가 이렇게 constructor 를 작성하게 되면 기존의 클래스 생성자를 덮어쓰게 됨
  • 그렇기에 리액트 컴포넌트가 지니고있던 생성자를 super를 통해 미리 실행하고 그 다음에 우리가 할 작업(state설정)을 해주는 것

메소드 작성

  handleIncrease = () => {
    this.setState({
      number: this.state.number + 1
    });
  }

  handleDecrease = () => {
    this.setState({
      number: this.state.number - 1
    });
  }
  • 컴포넌트에서 메소드는 다음과 같은 형식으로도 작성할 수 있음
  handleIncrease() {
    this.setState({
      number: this.state.number + 1
    });
  }

  handleDecrease() {
    this.setState({
      number: this.state.number - 1
    });
  }
  • 이렇게 하면 나중에 버튼에서 클릭이벤트가 발생 했을 때, this가 undefined로 나타나서 제대로 처리되지 않게 됨
  • 이는 함수가 버튼의 클릭이벤트로 전달이 되는 과정에서 “this”와의 연결이 끊겨버리기 때문
  • 이를 해결하려면 constructor에서 아래처럼 작성하거나 이전에 작성한 코드처럼 아예 화살표 함수형태로 하면 this가 풀리지 않음.
  constructor(props) {
    super(props);
    this.handleIncrease = this.handleIncrease.bind(this);
    this.handleDecrease = this.handleDecrease.bind(this);
  }

setState

  • state 에 있는 값을 바꾸기 위해서는, this.setState 를 무조건 거쳐야함. 리액트에서는, 이 함수가 호출되면 컴포넌트가 리렌더링 되도록 설계
  • setState는, 객체로 전달되는 값만 업데이트
  state = {
    number: 0,
    foo: 'bar'
  }
  • this.setState({ number: 1 }); 을 하게 된다면, foo 는 그대로 남고, number 값만 업데이트
  • setState 는 객체의 깊숙한곳 까지 확인하지 못함. 예를들어서, state가 아래와 같이 설정되어있다고 가정
  state = {
    number: 0,
    foo: {
      bar: 0,
      foobar: 1
    }
  }
  • 아래와 같이 작성해도 foobar값이 업데이트 되지 않음.
  this.setState({
    foo: {
      foobar: 2
    }
  })
  this.setState({
    number: 0,
    foo: {
      ...this.state.foo,
      foobar: 2
    }
  });
  • …은 자바스크립트의 전개연산자임. 기존의 객체안에 있는 내용을 해당 위치에다가 풀어준다는 의미
  • 그 다음에 우리가 설정하고 싶은 값을 또 넣어주면 해당 값을 덮어쓰게 됨
  • 이러한 작업이 꽤나 귀찮으므로, 나중에는 immutable.js 혹은 immer.js 를 사용하여 이 작업을 좀 더 간편하게함

setState에 객체 대신 함수를 전달하기

  • 기존 작성 코드
  this.setState({
    number: this.state.number + 1
  });
  • 변경된 코드
  this.setState(
    (state) => ({
      number: state.number
    })
  );
  this.setState(
    ({ number }) => ({
      number: number + 1
    })
  );
  • (state)가 ({number})가 됐는데 “비구조화 할당”이라는 문법
  • 아래와 같이 사용할 수 있음
  const { number } = this.state;
  • 기존에 작성했던 함수를 각각 다른 방식으로 구현
    handleIncrease = () => {
      const { number } = this.state;
      this.setState({
        number: number + 1
      });
    }

    handleDecrease = () => {
      this.setState(
        ({ number }) => ({
          number: number - 1
        })
      );
    }

이벤트 설정

  • 리액트에서 이벤트 함수를 설정할때 html 과 다른 사항
    1. 이벤트이름을 설정 할 때 camelCase 로 설정
    2. 이벤트에 전달해주는 값은 함수 만약에 onClick={this.handleIncrease()} 이런식으로 하게 된다면, 렌더링을 할 때 마다 해당 함수가 호출
    3. 렌더링 -> 함수 호출 -> setState -> 렌더링 -> 함수 호출 -> 무한반복이 돼버림
    4. 렌더링 함수에서 이벤트를 설정할 때 만든 메소드를 호출하면 안됨

Categories:

Updated:

Comments