[React][리액트를 다루는 기술] 컴포넌트 - state

uddi·2023년 3월 14일
0

React

목록 보기
6/16

state 란?

이전 포스트에서 설명했듯 props는 부모 컴포넌트에서 바꿔줘야 하고, 자식 컴포넌트에서는 전달받은 값을 바꿀 수 없다.

그렇다면 컴포넌트 내부에서 직접 값을 바꾸기 위해서는 어떻게 해야 할까?
👉 state를 사용하면 된다

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

리액트에는 두 가지 종류의 state가 있는데, 하나씩 알아보도록 하자

클래스형 컴포넌트의 state

import { Component } from 'react';

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

컴포넌트에 state를 설정할 때는 constructor 메서드 안에 작성한다.
constructor 메서드는 컴포넌트의 생성자 메서드인데, 반드시 super(props)를 호출해줘야 한다.

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

위 예시처럼 컴포넌트의 state는 객체 형식이어야 한다.

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

render 함수에서 현재 state를 조회할 때는 this.state를 조회하면 된다.
onClick 같은 이벤트에 함수를 넣어 줄 때는 화살표 함수 문법을 사용해서 넣어줘야 한다.
state 값을 변경하기 위해서는 this.setState 함수를 사용해야 한다.

state 객체 안에 여러 값이 존재할 때

state 객체 안에는 다음과 같이 여러 값이 존재할 수 있다.

constructor(props) {
    	super(props);
        this.state = {
        	number: 0, 
            fixedNumber: 0
        };
    }

만약 fixedNumber state의 값을 변경하고 싶다면 this.setState 함수의 인자로 넣어줘야 한다.

this.setState 함수는 인자로 전달된 객체 안에 들어 있는 값만 바꿔 준다.

state를 선언하는 또다른 방법

constructor 메서드 안에서 state의 초깃값을 지정해주는 방식 말고도 아래 예시처럼 state의 초깃값을 지정해 줄 수 있다.

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

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

this.setState를 사용해서 state 값을 업데이트할 때는 비동기적으로 업데이트된다.

다음과 같이 코드를 구현했다고 할 때 click 이벤트가 발생하면 number의 값이 몇 씩 오르게 될까?

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

대부분의 사람들은 this.setState를 두 번 사용해줬으니 2씩 증가할 것이라고 생각할 것이다. 하지만 값은 1씩 증가한다.
👉 this.setState를 사용한다고 해서 state 값이 바로 바뀌지는 않기 때문

해결법

this.setState를 사용할 때 객체 대신 함수를 인자로 넣어주면 된다.

this.setState에 함수를 인자로 넣는다면 코드는 이런 형태가 된다.

this.setState((prevState, props) => {
	return {
    	// 업데이트 내용
    }
})

prevState는 기존상태, props는 현재 지니고 있는 props를 가리키는데, 업데이트하는 과정에서 props가 필요하지 않다면 생략해도 된다.

처음 예시를 다시 함수를 인자로 넣은 형태로 고쳐보자

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

두개의 this.setState 함수 코드의 기능은 똑같다.
차이점이 있다면 두번째 this.setState는 함수에서 바로 객체를 반환한다는 것이다.

화살표 함수에서 값을 바로 반환하고 싶으면 코드 블록 { }을 생략하면 된다.

이제는 값이 2씩 잘 증가할 것이다!

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

setState를 사용하여 값을 업데이트하고 난 다음 특정 작업을 하고 싶을 때는 setState의 두 번째 파라미터로 콜백함수를 등록하면 된다.

this.setState(
	{
    	number: number + 1
    },
    () => {
    	console.log('방금 setState가 호출됨');
        console.log(this.state);
    }
);

함수 컴포넌트의 useState

리액트 16.8 이후 버전부터 useState 함수를 사용하여 함수 컴포넌트에서도 state를 사용할 수 있게 되었다.
(이 과정에서 Hooks을 사용하게 됨)

배열 비구조화 할당

배열 비구조화 할당은 객체 비구조화 할당과 비슷하다.
👉 배열 안에 들어 있는 값을 쉽게 추출할 수 있게 해 주는 문법

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

위 코드를 배열 비구조화 할당을 사용하면

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

이렇게 훨씬 간단하고 깔끔하게 표현할 수 있다.

useState

본격적으로 useState에 대해 알아보자

import { useState } from 'react';

const [message, setMessage] = useState('');

위 예시처럼 useState 함수의 인자에는 상태의 초깃값을 넣어 준다.
클래스형 컴포넌트에서와 달리 useState에서는 초깃값으로 반드시 객체가 아니어도 된다.

함수를 호출하면 배열이 반환되는데, 첫 번째 원소는 현재 상태고 두 번째 원소는 상태를 바꿔주는 함수다.
상태를 바꿔주는 함수는 setter 함수라고 부른다

또한 useState는 한 컴포넌트에서 여러 번 사용해도 된다.

state 사용 시 주의사항

함수, 클래스형 컴포넌트에서 공통적으로 staate의 값을 바꾸기 위해서는 setState 또는 useState를 통해 전달받은 setter 함수를 사용해야 한다.

그렇다면 배열이나 객체를 업데이트 할 때는 어떻게 해야 할까?
👉 배열이나 객체의 복사본을 만들어 값을 업데이트한 후, 해당 복사본의 상태를 업데이트 시킨다.

먼저 객체를 업데이트 시키는 예시를 보자

const object = { a; 1, b: 2, c: 3 };
const nextObjext = { ...object, b: 4 };

이처럼 객체를 복사할 때는 spread 연산자를 사용하여 처리한다.

다음은 배열을 업데이트하는 예시다.

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);
nextArray.map(item => (item.id === 1 ? { ...item, value: false } : item));

위와 같이 배열을 복사할 때는 배열의 내장 함수들을 사용한다.

정리

props와 state는 컴포넌트에서 사용하거나 렌더링할 데이터를 담고 있어 비슷해 보일 수 있지만 매우 다르다.

  • props는 부모 컴포넌트가 설정하는 것
  • state는 컴포넌트 자체적으로 지닌 값 👉 컴포넌트 내부에서 값 업데이트 가능

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

props에 대해 궁금하다면 이전 포스팅을 참고하면 된다!

profile
거북이는 느리지만 결국 결승선을 통과한다

0개의 댓글