부처3 2020. 5. 17. 12:12

3.1 클래스형 컴포넌트

클래스형 컴포넌트 특징

  • state 기능 및 라이프사이클 기능을 사용할 수 있음
  • 임의 메서드를 정의할 수 있음.
  • render 함수가 꼭 있어야 함. 

함수형 컴포넌트 특징

  • 클래스형 컴포넌트보다 선언하기 편함
  • 메모리 자원을 클래스형 컴포넌트보다 덜 사용함.
  • 빌드 후 배포할 때도 클래스형 컴포넌트와 비교했을 때 결과물의 크기가 더 작음.
  • state 및 라이프사이클 API를, Hooks라는 기능을 통해 간접 구현( v16.8)
  • 리액트 공식 매뉴얼에서 함수형 컴포넌트와 Hooks를 사용하도록 권장.
1.함수형
import React from "react";
import './App.css';

function App() {
  const name = '리액트';
  return <div className="react">{name}</div>
}

export default App;

2.클래스형
import React, {Component} from "react";
import './App.css';

class App extends Component {
  render() {
    const name = 'react';
    return <div className="react">{name}</div>
  }
}

export default App;

3.2 첫 컴포넌트 생성

//화살표 함수로 함수형 컴포넌트 구현
import React from "react";

const MyComponent = () => {
    return <div>화살표형 함수로 컴포넌트 구현</div>;
};

export default MyComponent;

일반 함수 vs 화살표형 함수

  • 화살표형 함수가 일반 함수를 완전히 대체할 수 있을까? (X) 둘의 용도가 다르므로 완전 대체는 불가능함.
    (화살표 함수는 주로 함수를 파라미터로 전달할 때 유용)
  • 함수 안에서 this를 호출했을 때 일반 함수는 자신이 종속된 객체를 가리키며, 화살표 함수는 자신이 종속된 인스턴스를 가리킴.
  • 화살표 함수는 값을 연산하여 바로 반환해야 할 때 사용하면 가독성을 높일 수 있음.
    (따로 { } 를 열어주지 않으면 연산한 값을 그대로 반환) 
    ex> function twice(value) {return value*2};  ===> const triple = (value) => value*3;
1.일반 함수에서의 this
function BlackDog() {
    this.name = '흰둥이';
    return {
        name: '검둥이',
        bark: function () {
            console.log(this.name + ': 멍멍!');
        }
    }
}

const blackDog = new BlackDog();
blackDog.bark(); //검둥이: 멍멍!


2.화살표 함수에서의 this
function WhiteDog() {
    this.name = '흰둥이';
    return {
        name: '검둥이',
        bark: () => {
            console.log(this.name + ': 멍멍!');
        }
    }
}

const whiteDow = new WhiteDog();
whiteDow.bark(); //흰둥이: 멍멍!

3.3 props

3.3.1 JSX 내부에서 props 랜더링

import React from "react";

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

export default MyComponent

3.3.2 컴포넌트를 사용할 때 props값 지정하기

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

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

export default App;

3.3.3 props 기본값 설정 : defaultProps

import React from "react";

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

MyComponent.defaultProps = {
    name: '기본 이름'
}

export default MyComponent

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

1.children 전달
import React from 'react';
import MyComponent from "./MyComponent";

const App = () => {
    return <MyComponent>리액트</MyComponent>
}

export default App;


2.porps로 받은 children 출력
import React from "react";

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

MyComponent.defaultProps = {
    name: '기본 이름'
}

export default MyComponent

3.3.5 비구조화 할당 문법을 통해 props 내부 값 추출하기

import React from "react";

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


export default MyComponent

3.3.6 propTypes를 통한 props 검증

컴포넌트에서 설정한 props가 propsTypes에서 지정한 형태와 일치하지 않는다면 브라우저 개발자 도구의 console 탭에 경고 메시지 출력됨.

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

const MyComponent = props => {
...
};

MyComponent.propTypes = {
    name : PropTypes.string,
    favoriteNumber : PropTypes.number.isRequired
}

export default MyComponent

3.3.7 클래스형 컴포넌트에서 props 사용하기

Class형 컴포넌트는 render함수 내부에서 this.props를 조회. defaultProps, propTypes는 함수형 컴포넌트와 같은 방식으로 하거나 class 내부에서 지정

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


class MyComponent extends Component {
    static defaultProps = {
        name: '기본 이름'
    }

    static propTypes = {
        name: PropTypes.string
    }

    render() {
        const {name, favoriteNumber, children} = this.props; //비구조화 할당
        return (
            <div>
                안녕하세요, 제 이름은 {name} 입니다.<br/>
                children 값은 {children}
                <br/>
                제가 좋아하는 숫자는 {favoriteNumber} 입니다.
            </div>
        );
    }
}

export default MyComponent

3.4 state

3.4.1 클래스형 컴포넌트의 state

state를 설정할 때 constructor 메서드(생성자)를 사용함.
constructor를 작성할 때는 반드시 super(props)를 호출해야 함.
(클래스형 컴포넌트가 상속하고 있는 리액트의 Component 클래스가 지닌 생성자 함수를 호출해줌.)

1.
import React, {Component} from "react";

class Counter extends Component {
    constructor(props) {
        super(props);
        //state의 초깃값 설정하기
        this.state = {
            number: 0,
            fixedNumber: 0
        };
    }

    render() {
        const {number, fixedNumber} = this.state;
        return (
            <div>
                <h1>{number}</h1>
                <h2>바뀌지 않는 값  : {fixedNumber}</h2>
                <button onClick={() => {
                    this.setState({number: number + 1});
                    //setState함수는 인자로 전달된 객체 안에 들어 있는 값만 바꾸어 줌.
                }}>
                    +1
                </button>
            </div>
        );
    }
}

export default Counter;


2.constructor 메소드를 선언하지 않고, state초깃값 설정
import React, {Component} from "react";

class Counter extends Component {
    state = {
        number: 0,
        fixedNumber: 0
    };

    render() {
        const {number, fixedNumber} = this.state;
        return (
            <div>
                <h1>{number}</h1>
                <h2>바뀌지 않는 값 : {fixedNumber}</h2>
                <button onClick={() => {
                    this.setState({number: number + 1});
                    //setState함수는 인자로 전달된 객체 안에 들어 있는 값만 바꾸어 줌.
                }}>
                    +1
                </button>
            </div>
        );
    }
}

export default Counter;


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

<button onClick={() => {
    this.setState(prevState => {
        return {number: prevState.number + 1}
    });
    // { } 생략해서 함수에서 값 바로 반환
    this.setState(prevState => ({number: prevState.number + 1}));
}}>
    +1
</button>

4.this.setState가 끝난 후 특정 작업 실행하기
<button onClick={() => {
    this.setState({number: number + 1}, () => {
        console.log('방금 setState가 호출되었습니다.');
        console.log(this.state);
    });
    //setState함수는 인자로 전달된 객체 안에 들어 있는 값만 바꾸어 줌.
}}>
    +1
</button>

3.4.2 함수형 컴포넌트에서 useState 사용하기

리액트 16.8이후 userState라는 함수를 사용하여 함수형 컴포넌트에서도 statef를 사용할 수 있게됨.
클래스형 컴포넌트에서의 state 초깃값은 객체 형태를 넣어줘야 하지만, useState는 값의 형태가 자유로움.
함수를 호출하면 배열이 반환됨. 첫 번째 원소는 현재 상태, 두 번째 원소는 상태를 바꿔주는 함수(세터).
배열 비구조화 할당을 통해 이름을 자유롭게 정해 줄 수 있음.

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;

3.5 state를 사용할 때 주의 사항

  • state값을 바꾸어야 할 때 setState혹은 useState를 통해 전달받은 세터 함수를 사용해야 함.
  • 배열이나 객체는 사본을 만들고 그 사본에 값을 업데이트한 후, 그 사본의 상태를 setState혹은 세터 함수를 통해 업데이트해야 함.
//객체 다루기
const object = {a: 1, b: 2, c: 3};
const newtObject = {...object, b: 2}; //사본을 만들어서 b값만 덮어 쓰기


//배열 다루기
const array = [
    {id: 1, value: true},
    {id: 2, value: true},
    {id: 3, value: true},
];
let nextArray = array.concat({id: 4});
nextArray.filter(item => item.id !== 2);
nextArray.map(item => (item.id === 1) ? {...item, value: false}, item);