Iriton's log

누구든지 하는 리액트 4편: props 와 state 본문

Frontend/Study

누구든지 하는 리액트 4편: props 와 state

Iriton 2024. 10. 16. 18:49

참고 자료

 

누구든지 하는 리액트: 초심자를 위한 리액트 핵심 강좌 | VELOPERT.LOG

이 튜토리얼은 리액트를 1도 모르는 사람들을 위해 작성되었습니다. 만약에 여러분이 리액트를 배우고 싶은데, 아직 뭐가 뭔지 잘 모르겠다! 그렇다면 이 튜토리얼을 진행하고 나면 리액트가 무

velopert.com

 

 

새 컴포넌트 만들기

src/component/test 폴더를 생성하여 MyName이라는 컴포넌트를 만든다.

import React, { Component } from 'react';

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

export default MyName;

 

import React, { Component } from "react";
import MyName from "./component/test/MyName";

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

export default App;

경로를 알맞게 설정하여 import 한다.

props 값은 name=”리액트” 이런 식으로 태그의 속성을 설정해 주듯이 한다.

 

(중간에 렌더링이 잘 안 돼서 오류가 뜨거나 흰 화면이 떴는데 새로고침 몇 번 하니까 됐다… 왜지?)

 

 

defaultProps

실수로 props를 빠뜨리거나 특정 상황에서 props를 일부러 비워야 할 때가 있다.

이때 props의 기본값을 설정해 줄 수 있는 게 defaultProps이다.

import React, { Component } from 'react';

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

export default MyName;

<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;

 

 

함수형 컴포넌트

단순히 props만 받아와서 보여 주기만 하는 컴포넌트의 경우 더 간편한 함수 형태의 문법으로 작성할 수 있다.

import React from 'react';

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

export default MyName;

함수형과 클래스형 컴포넌트의 주요 차이점은, state와 LifeCycle이 빠져 있다는 점이다.

아직 배우진 않았지만 컴포넌트 초기 마운트가 아주 미세하게 빠르고 메모리 자원을 덜 사용한다. 미세한 차이이기에 컴포넌트를 무수히 많이 렌더링 하게 되는 게 아니라면 성능적으로 큰 차이는 없다.

 

 

state

동적 데이터를 다룰 땐 state를 사용한다.

에제 실습을 위해 Counter 라는 새로운 컴포넌트를 생성한다.

 

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 정의가 가장 먼저 보인다.

state를 정의할 때는 class fields 문법을 사용한다.

class fields를 사용하지 않는다면 다음과 같이 사용한다.

 

import React, { Component } from 'react';

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

  ...

 
}

확실히 class fields를 사용하는 것이 편해 보인다.

위 코드의 constructor에서 super(props)를 호출한 이유는 우리가 컴포넌트를 만들게 되면서 Component를 상속했으며 이렇게 constructor을 작성하게 되면 기존의 클래스 생성자를 덮어쓰게 된다. 그래서 React 컴포넌트가 지니고 있던 생서아를 super를 통해 미리 실행하고 다음 작업할 (state 설정)을 해 주는 것이다.

만약 class fields도 사용하고 constructor도 사용하게 된다면 class fields가 먼저 실행되고 그 다음에 constructor에서 설정된 것이 나온다.

 

메소드 작성

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

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

해당 메서드는 나중에 클릭 이벤트가 발생했을 때 쓰일 메서드로, state.number의 값을 +-1 하는 작동을 한다.

setState

this.setState로 state에 있는 값을 바꾼다. 리액트에서는 이 함수가 호출되면 컴포넌트가 리렌더링 되도록 설계되어 있다. setState는 객체로 전달되는 값만 업데이트 해 준다.

 

지금은 state에 number 값밖에 없지만 만약에 다음과 같은 다른 값이 있다고 가정해 보면

 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
  }
})

이렇게 하면 기존의 foo 객체가 바뀌어 버린다.

 

{
  number: 0,
  foo: {
    foobar: 2
  }
}
// bar 값이 사라짐.

이벤트 설정

 

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

버튼이 클릭되면 준비한 함수가 호출되도록 설정한다.

 

 

주의할 점

  • 이벤트 이름은 camelCase 로 설정해야 한다. onclick이 아닌 onClick 이런 식으로
  • 이벤트에 전달해 주는 값은 함수여야 한다. 만약 onClick={this.handleIncrease()} 이런 식으로 하게 되면 렌더링을 할 때마다 해당 함수가 호출된다. 렌더링→ 함수 호출 → setState → 렌더링 → 함수 호출 → 무한 반복… 이렇게 된다. 그렇기에 렌더링 함수에서 이벤트를 설정할 때 만든 메소드를 호출하지 말아야 한다.
    • 함수를 “호출” 하는 게 아니라 함수의 “참조를 전달”해야 한다는 것이 중요하다.
    • onClick={this.handleIncrease}처럼 함수 참조를 전달해야 하며, 이는 이벤트가 발생할 때만 함수가 호출되도록 한다.
    • 함수에 인자를 전달하고 싶을 땐 화살표 함수를 사용하여 이벤트 발생 시에만 함수가 실행되도록 해야 한다.
    • <button onClick={() => this.handleIncrease(10)} />

 

 

App.js

import React, { Component } from "react";
//import MyName from "./component/test/MyName";
import Counter from "./component/test/Counter";

class App extends Component {
  render() {
    return <Counter />;
  }
}

export default App;

 

 

Comments