공부 기록

[react] props, state

by 너나나

저번 게시글에서 짧게 언급만 하고 넘어간 props와 state!!! 를 좀 더 자세히 알아보자!!!

props

props는 properties를 줄인 표현으로 컴포넌트 속성을 설정할 때 사용하는 요소이다. props 값은 해당 컴포넌트를 불러와 사용하는 부모 컴포넌트에서 설정할 수 있다!!!

 

MyComponent라는 컴포넌트를 하나 만들어서 이 컴포넌트에 name이라는 props를 렌더링하도록 설정해보자!!

import React from 'react';

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

export default MyComponent;

이러면 이제 이 컴포넌트를 사용할 부모 컴포넌트에서 이 props값을 설정해줄 수 있다!! 이때 props값은 { } 기호로 감싸준다.

 

App컴포넌트에서 MyComponent를 사용한다고 해보자!!!!

import React from "react";
import './App.css';
import MyComponent from "./MyComponent";

const App = () => {
 return <MyComponent name="React" />
}
export default App;

이렇게 App에서 MyComponent를 사용할때 name이라는 props에 값을 명시해서 사용할 수 있다!!!

그러고 실행시켜보면

안녕하세요, 제 이름은 React입니다. 가 잘 출력되는것을 볼 수 있다!!

 

만약 App에서 name 값을 주지않고 그냥 return <MyCompoent />만 사용하면

안녕하세요, 제 이름은 입니다. 이렇게 출력이 된다!!! name값을 명시하지 않았으니까 그냥 없이 출력되는거다!!

 

지금처럼 props 값을 따로 지정하지 않았을 때 보여 줄 기본값을 설정해주는 defaultProps라는 친구가 있다.

다시 MyComponent.js로 가서

import React from 'react';

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

MyComponent.defaultProps = {
    name: '김너나'
};

export default MyComponent;

이렇게 defaultProps로 name의 디폴트 값을 설정해보자!!! 그러면

안녕하세요, 제 이름은 김너나입니다. 이렇게 잘 출력되는 것을 볼 수 있다!!! 그러니까 props 값을 부모컴포넌트에서 명시해주지 않았을때 기본으로 출력하고 싶은 값을 defaultProps를 사용해 표시해주면 된다!!!

 

컴포넌트를 사용할 때 컴포넌트 태그 사이에 내용을 쓸 수 있다.

App.js에서

import React from "react";
import './App.css';
import MyComponent from "./MyComponent";

const App = () => {
 return <MyComponent>잠온다</MyComponent>
}
export default App;

이렇게 컴포넌트 태그 사이에 잠온다 라는 내용을 썼다!!! 이 친구를 MyComponent에서 사용하고 싶으면 props.children값을 이용하면 된다. 

 

그러니까 MyComponent.js에서

import React from 'react';

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

MyComponent.defaultProps = {
    name: '김너나'
};

export default MyComponent;

이렇게 props.children이라는 친구를 쓰면

안녕하세요, 제 이름은 김너나입니다. 너무 잠온다 이렇게 출력이 잘 된 것을 볼 수 있다!!!!

그러니까 부모 컴포넌트에서 컴포넌트 태그 사이에 쓴 내용을 사용하고 싶으면 자식 컴포넌트에 props.children을 사용하면 된다는 뜻!!!!

 

그런데 지금 MyCompoenent에서 props 값을 사용하려면 props.name, props.children 이렇게 앞에 계속 props. 이라는 키워드를 사용해줘야한다ㅠㅠㅠㅠ 너무 불편해!!!!!

이때 MyComponent.js에서

const { name, children } = props; 이 한줄만 추가해주면 편안하게 그냥 name, children으로 props. 키워드를 쓰지 않고도 사용할 수 있다!!

import React from 'react';

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

MyComponent.defaultProps = {
    name: '김너나'
};

export default MyComponent;

이렇게 객체에서 값을 추출하는 문법을 비구조화 할당 이라고 부른다. 구조 분해 문법이라고 하는데 함수의 파라미터 부분에서 사용할 수도 있다!! 

import React from 'react';

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

MyComponent.defaultProps = {
    name: '김너나'
};

export default MyComponent;

이렇게 props를 사용하기 위해 컴포넌트 함수의 파라미터에 props를 쓰고 그 밑에 const { name, children } = props; 를 명시해주는게 아니라 다이렉트로 그냥 함수 파라미터에 사용할 props들을 적어줘도 된다는 것이다!!

 

내 이름과 번호를 보여주는 컴포넌트를 작성해본다고 생각해보자!!!

그러면 MyComponent.js를

import React from 'react';

const MyComponent =({name, phoneNumber}) => {
    return (
    <div>
        안녕하세요, 제 이름은 {name}입니다. <br/>
        내 전화번호는 { phoneNumber }입니다.
    </div>
    );
};

MyComponent.defaultProps = {
    name: '김너나'
};

export default MyComponent;

대충 이렇게 작성해주고

App.js에

import React from "react";
import './App.css';
import MyComponent from "./MyComponent";

const App = () => {
 return <MyComponent phoneNumber="010-1234-5678" />
}
export default App;

이렇게 해주면 

안녕하세요, 제 이름은 김너나입니다.
내 전화번호는 010-1234-5678입니다.

이렇게 잘 나온다!!! 그런데 상식적으로 번호를 표시하려면 - 이 짝대기도 들어가니까 phoneNumber에는 문자열(string)타입을 받아야할것이다!! 이렇게 props 타입을 지정해주고 싶으면 propTypes를 사용해주면 된다. defaultProp를 설정하는 것과 비슷하다!!

MyComponent.js를

import React from 'react';
import PropTypes from 'prop-types';

const MyComponent =({name, phoneNumber}) => {
    return (
    <div>
        안녕하세요, 제 이름은 {name}입니다. <br/>
        내 전화번호는 { phoneNumber }입니다.
    </div>
    );
};

MyComponent.defaultProps = {
    name: '김너나'
};

MyComponent.propTypes = {
    phoneNumber: PropTypes.string
}

export default MyComponent;

이렇게 작성해주고 App.js에 phoneNumber을 문자열이 아닌 숫자로 전달해보자!!

import React from "react";
import './App.css';
import MyComponent from "./MyComponent";

const App = () => {
 return <MyComponent phoneNumber={1234} />
}
export default App;

이러면 

안녕하세요, 제 이름은 김너나입니다.
내 전화번호는 1234입니다.

이렇게 아무렇지 않게 출력이 되는거 같지만 개발자 도구의 console을 보면

이렇게 열심히 경고 메세지를 띄워주고 있따는 것을 볼 수 있다!!!! 문자열로 phoneNumber을 주면 경고 메시지가 사라진다!!

 

지금까지 계속 함수형 컴포넌트에서 props를 사용했는데 클래스형 컴포넌트에서 props를 사용하려면 어떻게 해야할까?!?!?! 바로바로 render 함수에서 this.props를 조회하면 된다!! 그리고 defaultProps와 propsTypes는 똑같은 방식으로 설정할 수 있다. 방금까지 열심히 쓴 MyComponent를 클래스형 컴포넌트로 바꿔보자!!

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class MyComponent extends Component {
    render() {
        const {name, phoneNumber} = this.props;
        return (
        <div>
            안녕하세요, 제 이름은 {name}입니다. <br/>
            내 전화번호는 { phoneNumber }입니다.
        </div>
        );
    }
};

MyComponent.defaultProps = {
    name: '김너나'
};

MyComponent.propTypes = {
    phoneNumber: PropTypes.string
}

export default MyComponent;

그냥 return을 render함수 안에 넣어주고 return 함수 바로 위에 사용할 props를 비구조화 할당 해주면 된다!!!

 

클래스형 컴포넌트에서 defaultProps와 propTyes을 class 내부에 지정해줄 수도 있다.

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class MyComponent extends Component {
    static defaultProps = {
        name: '김너나'
    }
    static propTypes = {
        phoneNumber: PropTypes.string
    }
    render() {
        const {name, phoneNumber} = this.props;
        return (
        <div>
            안녕하세요, 제 이름은 {name}입니다. <br/>
            내 전화번호는 { phoneNumber }입니다.
        </div>
        );
    }
};

export default MyComponent;

state

위에서 계속 사용한 props는 컴포넌트가 사용되는 과정에서 부모 컴포넌트가 설정하는 값이며, 컴포넌트 자신은 해당 props를 읽기 전용으로만 사용할 수 있다. 그러니까 props를 해당 컴포넌트에서 변경할 수 없고 부모 컴포넌트에서만 바꿀 수 있다. 예를 들어 위 코드의 App 컴포넌트에서 MyComponent를 사용할 때 props를 바꾸어 주어야 값이 변경될 수 있고 MyComponent에서는 전달받은 name과 phoneNumber 값을 직접 바꿀 수 없다!!

 

이번에 알아볼 state는 컴포넌트 내부에서 바뀔 수 있는 값을 의미한다!!!

리액트에는 두 가지 종류의 state가 있다. 하나는 클래스형 컴포넌트가 가지고 있는 state이고, 다른 하나는 함수형컴포넌트에서 useState라는 함수를 통해 사용하는 state 이다!!

 

먼저 클래스형 컴포넌트의 state를 알아보자!!! Counter.js라는 새로운 컴포넌트를 생성할것이다!!

코드를 보자!!!

import React, {Component} from 'react';

class Counter extends Component {
    constructor(props) {
        super(props);
        // state 초깃값 설정
        this.state = {
            number: 0
        };
    }
    render() {
        const { number } = this.state; // state를 조회할 때는 this.state로 조회
        return (
            <div>
                <h1>{number}</h1>
                <button
                    onClick={() => {
                        //this.setState를 사용하여 state에 새로운 값을 넣을 수 있음
                        this.setState({number:number + 1});
                    }}>
                    +1
                </button>    
            </div>
        );
    }
}

export default Counter;

컴포넌트에서 state를 설정할 때는 constructor 메서드를 작성하여 사용한다. 이 친구는 컴포넌트의 생성자 메서드인데 클래스형 컴포넌트에서 constructor을 작성할 때는 반드시 super(props)를 호출해야한다. 이 함수가 호출되면 현재 클래스형 컴포넌트가 상속받고 있는 리액트의 Component 클래스가 지닌 생성자 함수를 호출해준다.

그 다음 this.state를 이용해 초깃값을 설정해준다. 컴포넌트의 state는 객체 형식이어야한다.

    constructor(props) {
        super(props);
        // state 초깃값 설정
        this.state = {
        	// 사용할 state들 초깃값을 넣어주면 됨
        };
    }

그러니까 클래스형 컴포넌트에서 state를 사용하려면 이 친구들을 먼저 써주면 된다!!!

내가 사용할 state number을 0으로 설정해줬다.

 

이제 render함수를 보자!!

render() {
    const { number } = this.state; // state를 조회할 때는 this.state로 조회
    return (
        <div>
            <h1>{number}</h1>
            <button
                onClick={() => {
                    //this.setState를 사용하여 state에 새로운 값을 넣을 수 있음
                    this.setState({number:number + 1});
                }}>
            	+1
            </button>    
        </div>
    );
}

this.setState라는 함수는 state 값을 바꿀 수 있게 해준다. onClick은 아직은 몰라도 되는데 onClick 함수 안에 this.setState함수를 사용해서 대충 버튼 누르면 this.setState({number:number+1}); 로 number이 1씩 증가하게 된다!!!

App.js를

import React from "react";
import './App.css';
import Counter from './Counter.js'

const App = () => {
 return <Counter />
}
export default App;

 이렇게 Counter 컴포넌트를 사용하게 바꿔주고 실행하면

+1을 누를때 마다 숫자가 1씩 증가하는게 잘 나온다!!!!

 

state 객체 안에는 여러 값이 있을 수도 있다!!! 버튼을 눌렀을때도 증가하지 않는 숫자 친구도 하나 만들어보자!!

import React, {Component} from 'react';

class Counter extends Component {
    constructor(props) {
        super(props);
        // stae 초깃값 설정
        this.state = {
            number: 0,
            fixedNumber: 0
        };
    }
    render() {
        const { number, fixedNumber } = this.state; // state를 조회할 때는 this.state로 조회
        return (
            <div>
                <h1>{number}</h1>
                <h2>나는 안바뀜!!! {fixedNumber} </h2>
                <button
                    onClick={() => {
                        //this.setState를 사용하여 state에 새로운 값을 넣을 수 있음
                        this.setState({number:number + 1});
                    }}>
                    +1
                </button>    
            </div>
        );
    }
}

export default Counter;

state에 fixedNumber이라는 안바뀌는 숫자를 나타내는 값을 추개줬다. onClick내부를 보면 this.setState는 number만 + 1 시키지 fixedNumber에는 전혀 관여를 안한다!! 그래서 실행시고 버튼을 눌러보면

이렇게 위에 친구만 바뀌고 밑에 친구는 바뀌지 않는다!!! 이처럼 state는 여러개를 설정할 수 있고 this.setState에 사용되는 state만 달라지는것을 알 수 있다!!

 

state 초깃값을 지정하기 위해 constructor 메서드를 선언했는데 다른 방식으로도 초깃값을 설정해줄 수 있다!!

import React, {Component} from 'react';

class Counter extends Component {
    state = {
        number: 0,
        fixedNumber: 0
    }
    render() {
        const { number, fixedNumber } = this.state; // state를 조회할 때는 this.state로 조회
        return (
            <div>
                <h1>{number}</h1>
                <h2>나는 안바뀜!!! {fixedNumber} </h2>
                <button
                    onClick={() => {
                        //this.setState를 사용하여 state에 새로운 값을 넣을 수 있음
                        this.setState({number:number + 1});
                    }}>
                    +1
                </button>    
            </div>
        );
    }
}

export default Counter;

이렇게

    constructor(props) {
        super(props);
        // state 초깃값 설정
        this.state = {
        	// 사용할 state들 초깃값을 넣어주면 됨
        };
    }

이 친구 말고 그냥 바로

state = {
    number: 0,
    fixedNumber: 0
}

이렇게 다이렉트로 쓰면 된다!!!!

 

this.setState가 끝난 후 특정 작업을 하고 싶을 떄는 setState의 두번째 파라미터로 콜백(callback)이라는 함수를 등록하면 된다.

Counter.js 의 button부분을 바꿔보자!!

                <button
                    onClick={() => {
                        //this.setState를 사용하여 state에 새로운 값을 넣을 수 있음
                        this.setState(
                            {
                                number:number + 1
                            },
                            () => {
                                console.log('방금 setState가 호출됨!!!');
                                console.log(this.state);
                            });
                    }}>

setState({number~}, ()=> { 다른거 실행}); 이렇게 하면 setState가 끝나고 다른 함수를 실행할 수 있다!!

이 코드를 개발자도구 console로 보면

이렇게 setState가 끝날때 마다 log를 출력하는 것을 볼 수 있다.

 

이번엔 함수형 컴포넌트에서 state를 사용하는 것을 알아보자!!! 래익트 16.8 이전 버전에서는 함수형 컴포넌트에서 state 사용이 불가능했는데 이후부터는 useState라는 함수를 사용하여 함수형 컴포넌트에도 state를 사용할 수 있게 되었다!! 사용법은 클래스형과 조금 다르다!! 이 과정에서 Hooks라는 친구를 사용하는데 지금은 useState만 알아보고 나머지는 다음번에!!!

 

먼저 배열 비구조화 할당이라는 것을 알아보자!!

const array = [1, 2];
const one = array[0];
const two = array[1];

array안에 있는 값을 one과 two에 담아주는 코드이다. 위 코드를 배열 비구조화 할당을 사용하면 다음과 같이 표현할 수 있다.

const array = [1, 2];
const [one, tow] = array;

 

이제 useState를 사용해보자!!! Say.js라는 파일을 생성한다!!

import React, { useState } from 'react';

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

    return (
        <div>
            <button onClick = {onClickEnter}>입장</button>
            <button onClick = {onClickLeave}>퇴장</button>
            <h1>{message}</h1>
        </div>
    )
};

export default Say;

App.js도 잊지말고 바꿔주기!!!

onClick 이벤트는 생각하지 말고 useState부분을 보자!!! useState 함수의 인자에는 초깃값을 넣어준다!! 함수를 호출하면 배열이 반환되는데 배열의 첫번째 원소는 현재 상태이고 두번째 원소는 상태를 바꿔주는 함수다. 이 함수를 Setter함수라고 부른다!! 그리고 배열 비구조화 할당을 통해 이름을 자유롭게 정해줄 수 있다.

그러니까 const[message, SetMessage] = useState(''); 여기서 message는 초깃값, SetMessage로 상태를 바꿀 수 있다!!

이름은 뭐 message를 하든 text를 하든 내 맘대로 하면 됨!!! 그래서 실행하고 입장 퇴장 버튼을 누르면 메세지가 바뀌는것을 볼 수 있따

입장 눌렀을때
퇴장 눌렀을때

한 컴포넌트에서 useState를 여러번 사용해도 된다!! Say.js를 수정해보쟈

import React, { useState } from 'react';

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

    const[color, SetColor] = useState('black');
    return (
        <div>
            <button onClick = {onClickEnter}>입장</button>
            <button onClick = {onClickLeave}>퇴장</button>
            <h1 style = {{ color }}>{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>
        </div>
    )
};

export default Say;

입장, 초록색 눌렀을때

 

이렇게 state와 props에 대해서 알아봤다!!!! state 값을 바꿀때는 무조건 setState나 useState를 사용해야한다!!!

 

끝!!!!!


참고 자료

 

누구든지 하는 리액트 1편: 리액트는 무엇인가 | VELOPERT.LOG

이 튜토리얼은 10편으로 이뤄진 시리즈입니다. 이전 / 다음 편을 확인하시려면 목차를 확인하세요. 프론트엔드 라이브러리 / 프레임워크 리액트는 정말 인기있는 프론트엔드 라이브러리입니다.

velopert.com

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

'study > react (2021 여름방학)' 카테고리의 다른 글

[react] 라이프사이클 메서드  (0) 2021.07.24
[react] 컴포넌트 반복  (0) 2021.07.18
[react] DOM에 이름 달기 ref  (0) 2021.07.17
[react] 이벤트 핸들링  (0) 2021.07.17
[react] 리액트 정리 - 1  (0) 2021.07.11

블로그의 정보

공부 기록

너나나

활동하기