State와 Props에 대해서
FE BE 개발 메모장/React

State와 Props에 대해서

우선 리액트를 사용할때는 없어선 안되는 필수적인 존재들이 state와 props이다. 리액트에서 데이터를 다룰 때 사용하는 개념이기 때문이다. 

 

props

React에서 props는 읽기 전용, 즉 변하지 않는 데이터이다. 상위(부모) 컴포넌트에서 하위(자식) 컴포넌트로 데이터를 넘겨줄 때 사용한다. 아래의 예제를 살펴보자

import React, { Component } from 'react';
import MyName from './MyName';

function App() {
  return (
    <div>
      <Hello name="coding" />
      <MyName name="숨쉬기" />
    </div>
  );
}

function Hello(props) {
  return <div>오늘도 하는 {props.name}</div>
}

export default App;

 우선 App라는 최상위 컴포넌트를 작성해주었다. 그리고 App()안에는 하위 컴포넌트가 존재한다. 이 두개의 하위 컴포넌트 <Hello />, <MyName /> 안에는 name이라는 prop이 들어있다. 이렇게 공통된 props를 전달하는 부모 컴포넌트를 공통부모라 부른다.

 

 React에서는 부모 컴포넌트에서 자식 컴포넌트로 Props를 전달하기 위해선 코드와 같이 <FuncName propsName = {??} /> 이런 양식으로 작성하는 하나의 약속을 가지고있다.

 

아래의 코드는 MyName이라는 공통된 부모를 가진 자식 컴포넌트 MyName에 name이라는 prop을 전달해주었다.

import React, { Component } from 'react';

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

export default MyName;

 

아래의 코드는 위에 class로 생성된 컴포넌트를 함수형으로 다시 표현해보았다.

import React, { Component } from 'react';

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

export default MyName;

 

defaultProps로 기본값 설정

컴포넌트에 props를 지정하지 않고, 기본적으로 사용할 값을 설정한다면 defaultProps를 설정하면 된다.

 

function App() {
  return (
    <div>
      <Hello name="coding" color="aqua" />
  	  <Hello color="green" />
    </div>
  );
}

function Hello({ color, name}) {
  return <div style={{color}}>오늘도 하는 {name}</div>
}

Hello.defaultProps = {
 name: '프로그래머'
}

export default App;

 

state(상태)

state는 컴포넌트에서 유동적인 데이터를 다룰때 사용한다. 즉, 시간이 지남에 따라 상태가 변하는 값이나, Input 같은 사용자 입력으로 특정 요소를 불러올 때, 상태를 사용해야 한다. 

 

state 객체를 사용하기 위해서는 생성자(constructor())안에 적어준다.  컴포넌트 생성자에서 spuer를 호출하기 전에는 this를 사용할수 없기 때문이다.

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

render(){
 return( 

 )
}

 

왜 super(porps)를 작성해야 하는가?

 React 컴포넌트를 작성할 때 항상 생성자 시작부분에 super(props)를 생성해주었다. 자바스크립트 언어적 제약사항으로 생성자에서 super를 호출하기 전까지는 this를 사용할 수 없다. 만약에 this를 사용할수 있게 된다고 가정하게 되면 코드는 더욱 복잡해지고 이해하기 어려울 거라고 해당 링크에서 설명해주고 있다.

 

하지만 왜 props가 인자로 전달되어야만 하는지 의문을 갖게된다. 그 이유는 React.Component 객체가 생성될 때 props 속성을 초기화 하기 위해 부모 컴포넌트에게 전달하기 때문이다.

props인자를 전달하지 않고 super()만 호출하게 되더라도 render() 나 다른 메소드에서 여전히 this.props를 사용할 수 있게된다.

extends Component(혹은 React.Component)를 통해 클래스나 함수 컴포넌트가 props 속성을 상속받기 때문이다.

 

App 클래스 내부에서는 중괄호를 통해

render() {
 return(
    <div>
     <App2 title={this.state.counter} />
    </div>
 );
}

이렇게 state의 값을 가져와 사용한다.

setState 

setState는 state의 값을 바꾸기 위해 this.setState를 반드시 거쳐야한다. React에서는 setState함수가 호출되면 컴포넌트가 리렌더링 되도록 설계되어있다.

 

간단한 예제로 카운터를 만들어보자

import React, { Component } from 'react';

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

우선 Counter라는 클래스를 선언 해주고 Component를 상속시켜주었다.

증가(Increase) 감소(Decrease) 메소드를 만들고 this.setState를 적용시켜보았다.

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

state의 0에 1을 더해주어 {number: 1}로 state가 셋팅된다.

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

state의 0에 1을 빼주어 {number: 0}으로 state가 셋팅된다.

이런식으로도 작성이 가능하다

handleIncrease = () => {
  const { number } = this.state; //비구조화 할당으로 this.state.number
  this.setState({
    number: number + 1
  });
}

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

이제 위에 작성한 코드를 바탕으로 웹페이지에 직접 렌더링할 render() 함수를 작성해준다.

render() {
  const { handleIncrease, handleDecrease } = this // this.handleIn..., this.handleDe..
  return(
     <div>
      <h1>카운터</h1>
      <h2>값: {this.state.number}</h2>     //자바스크립트 값을 넣어줄땐 중괄호 안에 담아준다.
      <button onClick={handleIncrease}>+</button>
      <button onClick={handleDecrease}>-</button>
     </div>
  );
 }
}

export default Counter;

위 코드에서 버튼을 클릭하게 되면 각각의 함수가 호출되도록 설정을 해주었다. (onClick으로 이벤트설정)

  <button onClick={handleIncrease}>+</button>
  <button onClick={handleDecrease}>-</button>

리액트에서는 항상 이벤트 이름을 작성해줄때 camelCase 규칙으로 작성해준다. 또한 이벤트에 전달해주는 값은 함수 여아한다.

만약 [ onClick={this.handleIncrease()} 이런식으로 함수로서가 아닌 메소드를 호출해줄 경우 렌더링을 할 때마다. 무한반복으로 호출이되버린다.

 

 

마지막으로 Dom을 거쳐 직접 렌더링 하기 위한 코드를 작성을 해준다.

import "./App.css";
import React, { Component } from "react";

import Counter from "./Counter"


function App() {
  return (
    <div id="Lego">
      <Counter />
    </div>
  );
}

 

이렇게 아주 간단한 카운터가 만들어졌다.