본문으로 바로가기

3. 컴포넌트

category React/리액트를 다루는 기술 2021. 1. 9. 01:55

3-1. 클래스형 컴포넌트


컴포넌트를 선언하는 방식은 두 가지이다

  • 함수형 컴포넌트
  • 클래스형 컴포넌트

위의 두 컴포넌트 모두 동일한 결과를 나타낸다

클래스형 vs 함수형


리액트의 버전이 올라가면서 현재는 둘의 차이점이 없다고 봐도 된다

아래는 리액트 공식 메뉴얼이다

결정적으로, Hook은 존재하는 코드와 함께 나란히 작동함으로써 점진적으로 적용할 수 있습니다.

그냥 함수형을 쓰자

3-3. props


props는 properties의 줄임말로 컴포넌트의 속성값이다

//in index.js
import React from 'react';
import ReactDOM from "react-dom";

const Test = ({name})=> {
    return (
        <div>
          <h1>{name}</h1>
        </div>
    );
}
ReactDOM.render(
    <Test name={'parents'} />,
    document.getElementById("root")
);
  • props값은 자신보다 상위 컴포넌트에서만 정해줄 수 있으며 그렇게 정해준 값은 읽기 전용값이 된다

props 기본값


defaultProps 객체에 값을 넣어두면 props값이 들어오지 않았을 때 넣어둔 값이 나온다

const Test = ({ name })=> {
    return (
        <div>
          <h1>{name}</h1>
        </div>
    );
}
Test.defaultProps = {
    name  : 'default'
}

ReactDOM.render(
    <Test />,
    document.getElementById("root")
);

children


props 값 중에는 태그와 태그 사이에 있는 값을 저장해 놓는 children이 값이 존재

const Test = ({ name,children })=> {
    return (
        <div>
            <h1>{name}</h1>  // Title
            <div>사잇값 : {children}</div> // '사잇값 : 이 값'
        </div>
    );
}

ReactDOM.render(
    <Test name={'Title'}> 이 값 </Test>,
    document.getElementById("root")
);

사잇값이 컴포넌트여도 가능

const Test = ({ name,children })=> {
    return (
        <div>
            <h1>{name}</h1>
            <div>사잇값 : {children}</div>
                                                                                    // '사잇값 :'
                                                                                    // 'im ch'
        </div>
    );
}

const Ch = ()=>{
    return(
        <div>im ch</div>

    )
}
ReactDOM.render(
    <Test name={'Title'}> <Ch/> </Test>,
    document.getElementById("root")
);

클래스형에서의 props


class Test extends Component{
    static defaultProps = {
        name : 'default'
    };

    render() {
        const {name, children} = this.props;
        return (
            <div>
                <h1>{name}</h1>
                <div>사잇값: {children}</div>
            </div>
        )
    }

}
ReactDOM.render(
    <Test name={'Title'}> 이 값 </Test>,
    document.getElementById("root")
);

위에서 다루었던 것들을 클래스에서도 동일하게 사용가능하다

3-4. state


위에서 살펴본 props의 특성을 다시 보자

props값은 자신보다 상위 컴포넌트에서만 정해줄 수 있으며 그렇게 정해준 값은 읽기 전용
값이 된다.

props에 비해 컴포넌트 자신이 가지고 있는 값이며 컴포넌트 내부에서 수정이 가능한 값을 state라고 부른다

3-4-1. 클래스형 컴포넌트의 state


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

    render() {
        let {number} = this.state;
        return(
            <div>
                <div>{number}</div>
                <button className={'plus'} onClick={this.clickHandler}>+</button>
            </div>
        );
    }

    clickHandler=()=>{
        this.setState({
            number : this.state.number+1
        });
    }
}
ReactDOM.render(
    <Counter />,
    document.getElementById("root")
);

이 컴포넌트는 내부에 0으로 초기화된 number를 가지고 onClick이벤트의 setState를 이용해서 +를 누를 때 마다 number가 1씩 올라간다

constructor를 선언할 때 super(props)를 해주지 않으면 클래스 내에서 this를 사용할 수 없다

아이러니하게도 constructor 자체를 선언하지 않으면 아래와 같이 리액트가 자동으로 this를 할당해주어서 사용할 수 있게된다

class Counter extends Component{
    state ={
        number : 0
    }
    render() {
        let {number} = this.state;
                ...
                ...
}

3-4-2. this.setState


위의 코드에서 clickHandler를 바꿔보자

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

우리의 예상대로라면 이 코드는 +버튼을 눌러 이벤트를 발생시켰을 때 0→3 되어야하지만 실제론 0→1이 된다

왜 이런걸까?

컴포넌트의 리렌더링과 setState의 비동기적 업데이트


다음 조건들을 알아보자

  • 컴포넌트가 리렌더링될 때 state의 값이 바뀐다
  • state의 값을 바꾸는 setState는 비동기적이기 때문에 외부 스택에 쌓여있다 마지막에 처리된다
    • 스택에 쌓여있는 setState들은 this.state의 값을 참조한다

살짝 감이 오는가..?

  1. number = 0 의 값을 바꾸기 위해 +를 누른다
  2. number의 값을 바꾸기 위해 호출된 setState들은 즉시 number의 값을 바꾸지않고 외부에서 대기하고 있다
  3. 외부 대기열에서 3개의 setState들은 모두 값이 0인 (this.state.)number을 참조하고 있다
    • number : number(0) + 1
    • number : number(0) + 1
    • number : number(0) + 1
  4. 비동기 호출이 모두 끝나면 결국 number은 1로 리렌더링 된다

updater


이를 해결하기 위해 setState에 updater함수를 넣어주면 된다

setState(updater(state, props), [callback])

컴포넌트가 리렌더링될 때 state의 값이 바뀐다

는 점을 상기하라

updater의 state는 this.state와는 다르며 이 state는 리렌더링될 때 컴포넌트가 참조하는 객체라고 보면된다

즉 리렌더링될 때 updater의 state로 바뀌는 것

clickHandler=()=>{
        this.setState((pre)=>{
            return {number : pre.number+1}
        });

        this.setState((pre)=>{
            return {number : pre.number+1}
        });
    }

여기서 받아오는 pre는 이전의 위와 비슷하게 작동하지만 3번의 부분이 다르다

  1. 외부 대기열에서 3개의 setState들은 (this.state.)number가 아닌 pre의 값을 참조한다(this.state.number은 계속해서 0이다)
  • number : pre(0) + 1
  • number : pre(1) + 1
  • number : pre(2) + 1

결과 값은 3으로 우리가 원하는 값을 얻었다

3-4-3. 함수형 컴포넌트의 useState



const Counter = ()=>{
    const [number,setNumber] = useState(0,); // number을 0으로 초기화

    const clickHandler = ()=>{
                //선언해둔 setNumber로 number를 증가
        setNumber((number) => number+1);
    }
    return (
        <div>
            <div>{number}</div>
            <button onClick={clickHandler}>+</button>
        </div>
    )
}

함수형 컴포넌트를 쓰자.

'React > 리액트를 다루는 기술' 카테고리의 다른 글

6. 컴포넌트 반복  (0) 2021.01.17
5. ref: DOM에 이름 달기  (0) 2021.01.15
4. 이벤트 핸들링  (0) 2021.01.12
2. JSX  (0) 2021.01.08
1. 리액트 시작  (0) 2021.01.06