목차
1. props 다시 정리

2. props 기본값 설정 : defaultProps

3. 태그 사이의 내용을 보여주는 children

4. 비구조화 할당 문법을 통한 props 내부 값 추출

5. proTypes를 통한 props 검증

6. state

7. this.setState에 객체 대신 함수 인자 전달-+

8. 함수컴포넌트에서 useState로 state 사용


1. props

앞에서도 말했지만 props는 properties를 줄인 표현으로 컴포넌트 속성을 설정할 때 사용하는 요소이다.

 

아래와 같은 구조일 경우 App Component가 부모 컴포넌트이고 MyComponent Component가 자식 컴포넌트이다.

props는 부모 컴포넌트에서 설정할 수 있다.

//App 컴포넌트
import MyComponent from "./MyComponent"; 

const App=()=> {
  return (
    <div>
     <MyComponent name="홍길동" />
    </div>
  );
}

export default App;
//MyComponent 컴포넌트
const MyComponent=(props)=>{

    return <div>안녕하세요, 제 이름은 {props.name}입니다.</div>;

}

export default MyComponent;

위와 같이 부모 컴포넌트에 name 속성을 홍길동이라고 줬더니 자식 컴포넌트에 props.name 자리에 홍길동이 잘 나타나게 된다.

이런식으로 name이든 다른 속성이든 props를 다르게 주면 다른 element(다른 맛 붕어빵)이 만들어진다.


2. props 기본값 설정 : defaultProps

//App 컴포넌트
import MyComponent from "./MyComponent"; 

const App=()=> {
  return (
    <div>
     <MyComponent />
    </div>
  );
}

export default App;

부모컴포넌트에서 속성값을 안주게 되면 아래와 같이 렌더링되는데

이렇게 props값을 안받았을 경우에 default(기본값)으로 어떤 이름이 나오게 설정할 수 있다.

그게 바로 defaultProps이다

//MyComponent 컴포넌트

const MyComponent=(props)=>{

    return <div>안녕하세요, 제 이름은 {props.name}입니다.</div>;

}

MyComponent.defaultProps={
    name: '이름없음'
}

export default MyComponent;


3. 태그 사이의 내용을 보여주는 children

리액트 컴포넌트를 사용할 때 컴포넌트 태그 사이의 내용을 보여주는 props를 children이라고 한다.

//MyComponent 컴포넌트

const MyComponent=(props)=>{

    return <div>안녕하세요, 제 이름은 {props.children}입니다.</div>;

}

export default MyComponent;
//App 컴포넌트
import MyComponent from "./MyComponent"; 

const App=()=> {
  return (
    <div>
     <MyComponent >애기</MyComponent>    
    </div>
  );
}

export default App;


4. 비구조화 할당 문법을 통한 props 내부 값 추출

현재 MyComponent에서 props 값을 조회할 때마다 props.name, props.children과 같이 props라는 키워드를 앞에 붙여 주고 있는데 이러한 작업을 편하게 하기위해 비구조화 할당 문법을 사용할 수 있다.

 

▶구조 분해 할당

배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식이다.

 

그 중에서 객체 구조 분해할당의 형태는 다음과 같다

var o = {p: 42, q: true};
var {p, q} = o;

위와 같이 선언해줌으로써 p와 q를 개별변수로 사용할 수 있게 된다.

p만 호출해도 42라는 값을 가져올 수 있다.

 

리액트에서도 마찬가지이다.

const{name, children}=props라고 해줌으로써

props.name, props.children이라고 명시를 하지 않고 그냥 name, children이라고만 해줘도 해당 값에 접근이 가능해진 것이다.

//MyComponent 컴포넌트

const MyComponent=(props)=>{
    const{name, children}=props;
    return <div>안녕하세요, 제 이름은 {name}이자 {children}입니다.</div>;

}

export default MyComponent;

또는

//MyComponent 컴포넌트

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

}

export default MyComponent;
//App 컴포넌트
import MyComponent from "./MyComponent"; 

const App=()=> {
  return (
    <div>
     <MyComponent name="길동">애기</MyComponent>    
    </div>
  );
}

export default App;

5. proTypes를 통한 props 검증

컴포넌트의 필수 props를 지정하거나 props의 타입(type)을 지정할 때는 proTypes를 사용한다.

proTypes를 사용하려면 코드 상단에 import 구문을 사용하여 불러와야 한다.

import ProTypes from 'prop-types';

 

아래와 같이 name 속성을 proTypes로 string으로 지정해놓으면 문자열이 아닌 다른 자료형일 경우 Console창에 에러메시지가 뜬다.

//MyComponent 컴포넌트
import ProTypes from 'prop-types';

const MyComponent=(props)=>{
    const{name, children}=props;
    return <div>안녕하세요, 제 이름은 {name}이자 {children}입니다.</div>;

}

MyComponent.proTypes={
    name:ProTypes.string
}

export default MyComponent;

 

▶isRequired를 사용하여 필수 proTypes 설정

필수로 입력돼야 하는 proType에 isRequired를 붙여준다.

//MyComponent 컴포넌트
import ProTypes from 'prop-types';

const MyComponent=(props)=>{
    const{name, children}=props;
    return <div>안녕하세요, 제 이름은 {name}이자 {children}입니다.</div>;

}

MyComponent.proTypes={
    name:ProTypes.string.isrequired
}

export default MyComponent;

 

▶클래스형 컴포넌트에서 props 사용

클래스형 컴포넌트에서 props를 사용할 때 render 함수에서 this.props를 조회하면 되며 defaultProps와 propTypes는 똑같은 방식으로 설정가능하다.

//MyComponent 컴포넌트
import {Component} from 'react';
import ProTypes from 'prop-types';

class MyComponent extends Component{
    render(){
    const {name, children}=this.props;

     return(<div>안녕하세요, 제 이름은 {name}이자 {children}입니다.</div>)};

}

MyComponent.proTypes={
    name:ProTypes.string.isrequired
}

export default MyComponent;

6. state

state는 컴포넌트 내부에서 바뀔 수 있는 값을 의미한다.

props는 부모 컴포넌트가 설정하는 값이며 컴포넌트 자신을 해당 props를 읽기 전용으로만 사용할 수 있고 props를 변경하려면 부모 컴포넌트에서 바꿔줘야하지만 state는 내부에서 바뀌게 만들 수 있는 것이다.

 

바뀔수 있는 state 초기값을 설정해주는 형태는 아래와 같다.

컴포넌트의 state는 객체 형식이어야 한다.

 

초기값을 설정해주는 방법은 constructor에 설정해주거나 constructor을 사용하지 않고 바로 state를 설정해주는 방법 2가지가 있다.

 this.state = {
      number: 0,
    };

 

클래스형 컴포넌트에서 constructor를 작성할 때는 반드시 super(props)를 호출해 주어야 하며 

이 함수가 호출되면 현재 클래스형 컴포넌트가 상속받고 있는 리액트의 Component 클래스가 지닌 생성자 함수를 호출해준다.

 

아래에 나오는 onClick이라는 이벤트는 클릭하면 onClick 다음에 오는 함수를 실행하라는 의미이며 이 함수를 넣어줄 때는 화살표 함수 문법을 사용하여 넣어줘야 한다.

 

함수 내부에서 this.setState라는 함수를 사용하는데 이 함수가 state 값을 바꿀 수 있게 해 준다.

 

//생성자에서 state 초기값 설정

import { Component } from 'react';

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      number: 0,
    };
  }
  render() {
    const { number } = this.state;
    return (
      <div>
        <h1>{number}</h1>
        <button
          onClick={() => {
            this.setState({ number: number + 1 });
          }}
        >
          +1
        </button>
      </div>
    );
  }
}

export default Counter;

 

//생성자 없이 state 초기값 설정

import { Component } from 'react';

class Counter extends Component {
    state = {
      number: 0,
    };
  
  render() {
    const { number } = this.state;
    return (
      <div>
        <h1>{number}</h1>
        <button
          onClick={() => {
            this.setState({ number: number + 1 });
          }}
        >
          +1
        </button>
      </div>
    );
  }
}

export default Counter;

state값에는 여러 값이 올 수 있다.

    state = {
      number: 0,
      fixNumber:0
    };

7. this.setState에 객체 대신 함수 인자 전달

아래 예제코드를 보면 onClick을 했을 대 setState가 2번 호출되도록 만들었다. 즉 +2씩 증가되도록 만들려고 한 것이다.

import { Component } from 'react';

class Counter extends Component {
    state = {
      number: 0,
    };
  
  render() {
    const { number } = this.state;
    return (
      <div>
        <h1>{number}</h1>
        <button
          onClick={() => {
            this.setState({ number: number + 1 });
            this.setState({ number: this.state.number + 1 });
          }}
        >
          +1
        </button>
      </div>
    );
  }
}

export default Counter;

그런데 실제 결과는 +1씩 증가되었다.

위와 같이 작성하면 this.setState를 2번 사용하는 것임에도 불구하고 1씩 더해지는데 this.setState를 사용한다고 해서 stae 값이 바로 바뀌는 것이 아니기 때문이다.

이런 문제점을 해결하기 위해서는 this.setState를 사용할 때 객체 대신 함수를 인자로 넣어주면 된다.

위 코드를 아래코드처럼 바꿔주면 된다.

import { Component } from 'react';

class Counter extends Component {
    state = {
      number: 0,
    };
  
  render() {
    const { number } = this.state;
    return (
      <div>
        <h1>{number}</h1>
        <button
          onClick={() => {
            this.setState({ number: number + 1 });
            this.setState(prevState=>{return {number:prevState.number+1}});
          }}
        >
          +1
        </button>
      </div>
    );
  }
}

export default Counter;

함수형태로 표현하는 방식은 2개가 있다.

prevState는 기존 상태를 의미하며 기존 상태의 number에 1을 더해주는 것이다.

 

이렇게 함수를 사용하면 즉시 반영이 돼서 위에 있는 코드는 +2씩 잘 증가되는 것을 볼 수 있다.

//1번째 방식
this.setState(prevState=>{return {number:prevState.number+1}}

//2번째 방식
this.setState(prevState=>({number:prevState.number+1}));

2번째 방식은 return을 쓰지 않고 바로 객체를 반환하는 것인데 이 방식을 쓰기 위해서는

function A(){}이런 형태일때 A()뒤에 나오는 중괄호(코드블럭)을 생략하고 써주며

객체를 반환하는 형태이므로 ( { } ) 객체를 나타내는 중괄호를 괄호로 감싸는 형태가 된다.

 

setState 작업이 끝난 후 또 다른 작업을 하고싶을 대는 setState의 두 번째 파라미터로 콜백함수를 등록하면 된다.

import { Component } from 'react';

class Counter extends Component {
    state = {
      number: 0,
    };
  
  render() {
    const { number } = this.state;
    return (
      <div>
        <h1>{number}</h1>
        <button
          onClick={() => {
            this.setState({ number: number + 1 },()=>{console.log("안녕")});
          }}
        >
          +1
        </button>
      </div>
    );
  }
}

export default Counter;


8. 함수컴포넌트에서 useState로 state 사용

과거에는 함수컴포넌트에서 state를 사용할 수 없었지만 지금은 useState라는 함수를 사용해 함수 컴포넌트에서도 state를 사용할 수 있다.

 

userState를 알아보기 전에 배열 비구조화 할당에 대해 먼저 알아본다.

 

▶배열 비구조화 할당

구조 분해 할당은 구문은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식이다.

아래와 같이 red, yellow, green이라는 개별 변수로 배열 값을 접근할 수 있는 것이다.

▶useState사용

아래의 형태는 state 초기값을 ''으로 넣어준 것이다.

배열 첫번째 원소인 message는 현재 상태를 나타내고

배열 두번째 원소인 setMessage는 상태를 바꿔주는 함수를 나타내며 세터함수라고 부른다.

    const [message,setMessage]=useState('');
import {useState} from 'react';

const Say=()=>{
    const [message,setMessage]=useState(''); //state값을 ''으로 초기화
    const onClickEnter=()=>setMessage('안녕하세요!'); //state 값을 안녕하세요로 변경
    const onClickLeave=()=>setMessage('안녕히 가세요!');
    return (<div>
        <button onClick={onClickEnter}>입장</button> {/*클릭하면 onClickEnter함수 호출*/}
        <button onClick={onClickLeave}>퇴장</button>
        <h1>{message}</h1>
    </div>);
}

export default Say;

▶useState여러번 사용

 

useState는 한 컴포넌트에 여러번 사용해도 상관없다.

다만 style을 usestate로 지정할 때는 배열의 첫번째 원소인 현재 상태를 CSS에 사용되는 키워드로 사용해야 하며 원래 CSS에서 font-size였던 것이 리액트 에서는 fontSize라고 표현해줘야한다. (카멜 표기+ -기호 제거)

import {useState} from 'react';

const Say=()=>{
    const [message,setMessage]=useState('');
    const onClickEnter=()=>setMessage('안녕하세요!');
    const onClickLeave=()=>setMessage('안녕히 가세요!');

    const [color,setColor]=useState('black');
    const [fontSize,setfontsize]=useState('');
    return (<div>
        <button onClick={onClickEnter}>입장</button>
        <button onClick={onClickLeave}>퇴장</button>
        <h1 style={{color,fontSize}}>{message}</h1>
        <button style={{color:'red'}} onClick={()=>setColor('red')}>빨간색</button>
        <button style={{color:'green'}} onClick={()=>setColor('green')}>초록색</button>
        <button style={{color:'blue'}} onClick={()=>setColor('blue')}>파란색</button>
        
        <button onClick={()=>setfontsize('50px')}>폰트증가</button>
        <button onClick={()=>setfontsize('20px')}>폰트감소</button>

    </div>);
}

export default Say;

 

props를 사용한다고 해서 값이 무조건 고정적이지는 않다. 부모 컴포넌트의 staste를 자식 컴포넌트의 props로 전달하고, 자식 컴포넌트에서 특정 이벤트가 발생할 때 부모 컴포넌트의 메서드를 호출하며 props도 유동적으로 사용할 수 있다.


출처

리액트를 다루는 기술- 김민준

 

+ Recent posts