본문 바로가기

프로그래밍/React

리액트(React) - 컴포넌트, props, state 간단 설명

리액트를 공부하면서 쓰는 글이니 오류가 있을 수 있습니다. 오류가 있다면 알려주시기 바랍니다.

 

컴포넌트(Compoent)

컴포넌트는 재사용이 가능한 React 엘리먼트를 반환하는 하나의 구성 요소이다.. 즉 리액트에서 뷰를 표현하는 하나의 구성요소입니다.

 

리액트 컴포넌트의 종류

리액트의 컴포넌트는 함수형 컴포넌트, 클래스형 컴포넌트가 존재합니다.

함수형 컴포넌트 -  JavaScript에서 사용하는 함수를 이용해 만든 컴포넌트입니다.

클래스형 컴포넌트 - ES6에서부터 지원하는 class 문법을 이용해 만든 컴포넌트입니다.

 

props

 

props는 properties의 줄임말이며 컴포넌트 속성을 설정하는 요소입니다. 해당 props 값은 컴포넌트를 사용할 때 설정이 가능합니다.

// Test.js - Component
import React from 'react';

const Test = (props) => {
    return (<div>멋진 리액트 컴포넌트 {props.text}<br/> 자식 값 {props.children}</div>)
};

export default Test;

// App.js
import Test from './Test';

function App() {
  return <Test text="히히">헤헤</Test>
}

export default App;

컴포넌트를 사용할 때 속성으로 넣어주면 해당 속성이 객체 만들어져 넘어갑니다.

 

컴포넌트 자식의 값을 나타내고 싶다면 props.children 속성을 사용하면 나타낼 수 있습니다.

 

만약 컴포넌트 속성의 기본값을 설정하고 싶다면 defaultProps를 선언해야 합니다.

// Test.js - Component
import React from 'react';

const Test = (props) => {
    return (<div>멋진 리액트 컴포넌트 {props.text}<br/> 자식 값 {props.children}</div>)
};

// 기본값 설정
Test.defaultProps = {
    text : "이것은 기본 텍스트"
}

export default Test;

// App.js
import Test from './Test';

function App() {
  return <Test>헤헤</Test>
}

export default App;

defaultProps 객체를 선언하고 props가 사용할 속성의 기본값을 지정해주면 됩니다.

 

props의 프로퍼티는 읽기 전용이기 때문에 컴포넌트에서 직접 props 프로퍼티에 접근하여 따로 변경이 불가능합니다.

 

이번에는 클래스형 컴포넌트의 props를 사용하는 법을 알아보겠습니다.

 

클래스형 컴포넌트는 this.props로 props를 사용할 수 있습니다. 또 클래스 내에 static으로 defaultProps 객체를 선언하고 기본값을 설정할 수 있습니다.

class Test extends Component {
    static defaultProps = {
        text: "이것은 기본 텍스트"
    }
    render() {
        return <div>멋진 리액트 컴포넌트 {this.props.text}<br/> 자식 값 {this.props.children}</div>
    }
}

export default Test;

 

props에는 propType 객체를 이용해 컴포넌트에서 사용되는 속성의 타입을 지정할 수 있습니다. 타입으로는 배열, 함수, 객체, 숫자 등이 있습니다. 해당 종류는 레퍼런스를 참고하시기 바랍니다.

class Test extends Component {
    static defaultProps = {
        text: "이것은 기본 텍스트"
    }

    static propType = {
        text: PropTypes.number
    }
    
    render() {
        return <div>멋진 리액트 컴포넌트 {this.props.text}<br/> 자식 값 {this.props.children}</div>
    }
}

export default Test;

속성의 값이 지정된 타입이 아니라면 경고가 나타납니다. 

 

state

props는 읽기 전용이기 때문에 컴포넌트 안에서 변경이 불가능합니다. 따라서 내부에서 변경이 가능한 것이 필요한데 이것이 바로 state속성입니다. 

 

state 속성은 props와 달리 내부에서 변경이 가능합니다.

 

state는 클래스형 컴포넌트에서 사용하는 state와 함수형 컴포넌트에서 useState메서드를 이용하는 state가 있습니다.

 

클래스형 컴포넌트 state

state를 변경하고 싶다면 setState메서드를 사용해서 state를 변경합니다. state를 초기값을 선언할 때는 constructor메서드(생성자)에서 객체로 선언하여 사용합니다.

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

class Test extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text: "컴포넌트",
    };
  }

  render() {
    return (
      <div>
        <h1>{this.state.text}</h1>
        <button
          onClick={() => {
            this.setState({ text: "변경했어요!" });
          }}
        >
          변경
        </button>
      </div>
    );
  }
}

export default Test;

//app.js
import Test from './Test';

function App() {
  return <Test></Test>
}

export default App;

예제에는 버튼을 클릭하면 setState메서드가 호출되어 state의 text값이 변경됩니다.

 

super메서드는 상속받은 클래스의 생성자를 호출합니다. 하위 클래스에서 생성자를 사용할 때 super를 호출하지 않으면 객체가 할당되지 않아 에러가 발생합니다.

 

setState메서드는 비동기로 작업을 처리합니다. 변경점이 있을 때마다 계속 다시 렌더링 하기에는 비용이 많이 들기 때문에 비동기로 작업을 처리합니다. 때문에 바로바로 업데이트가 되지 않을 수 있습니다.

 

따라서 여러 개의 setState메서드를 사용하여 최신의 값으로 state값을 사용해야 한다면 함수를 인수로 넘겨 사용해야 합니다.

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

class Test extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text: "컴포넌트",
    };
  }

  render() {
    return (
      <div>
        <h1>{this.state.text}</h1>
        <button
          onClick={() => {
            this.setState((state, props) => {
                return {
                	text: state.text + " " + props.name + " 이였어요!"
                };
            });
        }}
        >
          변경
        </button>
      </div>
    );
  }
}

export default Test;

//app.js
import Test from './Test';

function App() {
  return <Test name="aaa"></Test>
}

export default App;

이렇게 setState메서드에 첫 번째 인수로 함수를 전달해주면 전달된 함수에서 최신의 state값과 최신의 props값을 사용할 수 있으므로 여러 setState의 작업을 수행할 때 순차적으로 값을 변경할 수 있습니다.

 

setState메서드의 두 번째 인수로 setState메서드의 작업이 끝났을 때 호출되는 콜백 함수를 전달할 수 있습니다.

render() {
    return (
      <div>
        <h1>{this.state.text}</h1>
        <button
          onClick={() => {
            this.setState((state, props) => {
                return {
                    text: state.text + " " + props.name + " 이였어요!"
                };
            },
            () => console.log("변경했어요!!"));
        }}
        >
          변경
        </button>
      </div>
    );
  }

 

함수형 컴포넌트 state

함수형 컴포넌트에서 state를 변경하고 싶다면 useState메서드를 사용해야 합니다. useState는 Hook에 있는 기능 중 하나입니다. 여기에서는 useState를 간단히 알아봅니다.

 

useState메서드는 반환 값으로 state값과 state를 변경할 수 있는 함수를 반환합니다.

const [text, setText] = useState("");

useState메서드의 인수로는 state의 초기값입니다. 따라서 위 코드로 보면 text가 초기 state값, setText가 state를 변경해주는 함수입니다.

import React, { useState } from 'react';

const Test2 = () => {
    const [text, setText] = useState("");
    const click = () => setText("클릭1");
    const click2 = () => setText("클릭2");

    return (
        <div>
            <button onClick={click}>버튼1</button>
            <button onClick={click2}>버튼2</button>
            <h1>{text}</h1>
        </div>
    )
}

export default Test2;

버튼을 클릭하면 setText가 호출되며 text값이 변경되고 다시 렌더링이 됩니다.

 

클래스형 컴포넌트와 함수형 컴포넌트에서 state를 변경할 때 state값을 직접적으로 변경해서는 안됩니다. 직접적으로 변경해도 값은 변경이 되나 다시 렌더링 되지 않습니다.

 

참고

https://ko.reactjs.org/docs/react-component.html

 

React.Component – React

A JavaScript library for building user interfaces

ko.reactjs.org

리액트를 다루는 기술(개정판) - 김민준 지음