본문 바로가기
JavaScript/React

React) state에 대해서 (클래스형 컴포넌트의 setState, 함수형 컴포넌트의 useState)

by 박채니 2022. 11. 1.
안녕하세요, 코린이의 코딩 학습기 채니 입니다.
[리액트를 다루는 기술]의 책을 참고하여 포스팅한 개인 공부 내용입니다.

 

state

- 컴포넌트 내부에서 바뀔 수 있는 값

 

props는 컴포넌트가 사용되는 과정에서 부모 컴포넌트가 설정하는 값으로, 컴포넌트 자신은 해당 props를 읽기 전용으로만 사용할 수 있습니다.

즉, props를 변경하려면 부모 컴포넌트에서 바꿔줘야 하는 것이죠.

그에 반면 state는 컴포넌트 내부에서 값을 변경할 수 있습니다.

 


클래스형 컴포넌트의 state

 

Counter.js

import { Component } from "react";

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

    render() {
        const {number} = this.state; // state 조회
        return (
            <div>
                <h1>{number}</h1>
                <button
                    // onClick을 통해 버튼이 클릭되었을 때 호출할 함수 지정
                    onClick={() => {
                        // this.setState로 state에 새로운 값 넣음
                        this.setState({number : number + 1});
                    }}
                >
                    +1
                </button>
            </div>
        );
    }
}

export default Counter;

 

App.js

import './App.css'
import Counter from './Counter';

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

export default App;

버튼을 누를 때마다 숫자가 증가하는 것을 확인할 수 있습니다.

 

* 코드 파악하기

constructor(props) {
    super(props);
    // state 초깃값 설정
    this.state = {
        number : 0
    };
}

컴포넌트에 state를 설정할 때 constructor 메소드를 작성하여 설정합니다.

클래스형 컴포넌트에서 constructor를 작성할 때는 반드시 super(props)를 호출해야 합니다.

호출 시 상속 받고 있는 리액트의 Component 클래스가 지닌 생성자 함수를 호출 해줍니다.

this.state로 초깃값을 설정해주었으며, 객체 형식이여야 합니다.

 

render() {
    const {number} = this.state; // state 조회
    return (
        <div>
            <h1>{number}</h1>
            <button
                // onClick을 통해 버튼이 클릭되었을 때 호출할 함수 지정
                onClick={() => {
                    // this.setState로 state에 새로운 값 넣음
                    this.setState({number : number + 1});
                }}
            >
                +1
            </button>
        </div>
    );
}

현재 state를 조회하고 싶다면, this.state를 조회해줍니다.

onClick으로 버튼 클릭 시 호출할 함수를 지정해주었고, this.setState 함수를 통해 state 값을 변경 시켜주었습니다.

(이벤트로 설정할 함수를 넣어줄 때는 화살표 함수 문법 사용)

 

state 객체 안에 여러 값이 있을 때

 

Counter.js

import { Component } from "react";

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

    render() {
        const {number, fixedNumber} = this.state; // state 조회
        return (
            <div>
                <h1>{number}</h1>
                <h2>바뀌지 않는 값 : {fixedNumber}</h2>
                <button
                    // onClick을 통해 버튼이 클릭되었을 때 호출할 함수 지정
                    onClick={() => {
                        // this.setState로 state에 새로운 값 넣음
                        this.setState({number : number + 1});
                    }}
                >
                    +1
                </button>
            </div>
        );
    }
}

export default Counter;

fixedNumber를 추가해주었고, 버튼 클릭 시 fixedNumber는 그대로 유지 / number는 +1 처리 해주었습니다.

fixedNumber는 그대로 0인 것을 확인할 수 있습니다.

this.setState 함수는 인자로 전달된 객체 안에 들어 있는 값만 변경해준다는 것을 알 수 있습니다.

 

state를 constructor에서 꺼내기

 

Counter.js

import { Component } from "react";

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

    render() {
        const {number, fixedNumber} = this.state; // state 조회
        return (
            <div>
                <h1>{number}</h1>
                <h2>바뀌지 않는 값 : {fixedNumber}</h2>
                <button
                    // onClick을 통해 버튼이 클릭되었을 때 호출할 함수 지정
                    onClick={() => {
                        // this.setState로 state에 새로운 값 넣음
                        this.setState({number : number + 1});
                    }}
                >
                    +1
                </button>
            </div>
        );
    }
}

export default Counter;

state 초깃값을 constructor에서 지정해주었는데, 이를 선언하지 않고도 state 초깃값을 설정해주었습니다.

훨씬 간편하죠!

 

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

 

Counter.js - button onClick

onClick={() => {
    // this.setState로 state에 새로운 값 넣음
    this.setState({number : number + 1});
    this.setState({number : this.state.number + 1});
}}

this.setState를 두 번 사용했지만 버튼을 클릭하면 1씩 증가됩니다.

그 이유는 this.setState를 사용한다고 해서 state 값을 바로 변경되지는 않기 때문인데요!

바로바로 값이 반영되고자 한다면 (2씩 증가), 객체 대신 함수를 인자로 넣어줍니다.

 

Counter.js - button onClick

<button
    // onClick을 통해 버튼이 클릭되었을 때 호출할 함수 지정
    onClick={() => {
        // this.setState로 state에 새로운 값 넣음
        this.setState((prevState, props) => {
            return {
                // 업데이트 하고 싶은 내용
                number : prevState.number + 1
            };    
        });

        // 위 코드와 아래 코드는 동일한 역할 수행
        // 아래 코드는 함수에서 바로 객체를 반환
        this.setState(prevState => ({
            number : prevState.number + 1
        }));
    }}
>
    +1
</button>

버튼을 클릭할 때마다 2씩 증가하는 것을 확인할 수 있습니다.

 

prevState는 기존 상태이고, props는 현재 지니고 있는 props를 가리킵니다.

 

this.setState가 끝난 후 특정 작업 실행하기

 

setState의 두 번째 파라미터로 콜백 함수를 등록하여 작업을 처리할 수 있습니다.

 

Counter.js - button onClick

<button
    // onClick을 통해 버튼이 클릭되었을 때 호출할 함수 지정
    onClick={() => {
        this.setState(
            {number : number + 1},
            // 콜백함수
            () => {
                console.log('방금 setState가 호출되었습니다.');
                console.log(this.state);
            }
        )
    }}
>
    +1
</button>

후처리가 잘 되는 것을 확인할 수 있습니다.

 


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

 

useState 사용하기

 

Say.js

import { 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;

useState 함수의 인자에 상태의 초깃값을 넣어 줍니다.

클래스형 컴포넌트에서는 state 초깃값을 객체 형태로 넣어주어야 했지만, useState는 객체가 아니어도 상관 없습니다.

 

useState 함수를 호출하게 되면 배열이 반환되고,

첫 번째 원소현재 상태 / 두 번째 원소상태를 바꾸어주는 함수 (setter) 입니다.

따라서 setter 함수에 의해 message가 변경되는 것이죠.

 

App.js

import Say from './Say';

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

export default App;

입장 클릭 시
퇴장 클릭 시

 

한 컴포넌트에서 useState 여러 번 사용하기

 

Say.js

import { 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 사용 시 주의 사항

  • state 값을 바꾸어야 할 때setState 혹은 useState를 통해 전달받은 세터 함수를 사용

 

잘못된 코드

// 클래스형 컴포넌트에서
this.state.number = this.state.number + 1;
this.state.array = this.array.push(2);
this.state.object.value = 5;

// 함수 컴포넌트에서
const [object, setObject] = useState({ a: 1, b: 1});
object.b = 2;

 

배열이나 객체를 업데이트 해야할 경우

- 배열이나 객체 사본을 생성사본에 값을 업데이트하여 사본의 상태를 setState 혹은 세터 함수를 통해 업데이트

// 객체 다루기
const object = { a: 1, b: 2, c: 3 };
const nextObject = {...object, b: 2}; // 사본을 만들어서 b 값만 덮어쓰기

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