state
React에서 state란, 컴포넌트 내부에서 바뀔 수 있는 값이다. React는 state의 변화를 반응적으로 감시하고, state가 변화한다면 페이지를 재렌더링한다. 즉, state는 동적인 웹페이지를 ‘선언적’으로 구성하는데 가장 중요한 요소이자. React의 핵심요소라고 볼 수 있다.
클래스형 컴포넌트
import React, { Component } from "react";
class Counter extends Component {
constructor(props) {
super(props);
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 });
this.setState({ number: number + 1 }, ()=>{
console.log("안뇽"); // 콜백함수 등록
});
}}
>
{" "}
+1{" "}
</button>
</div>
);
}
}
export default Counter;
클래스형 컴포넌트의 state는 객체 형식이어야 하며, constructor 메서드(생성자)에서 설정한다. 생성자에서 설정한 state는 이후로 this.state로 접근할 수 있으며, 값을 바꾸는 경우엔 setState() 함수를 사용한다. setState()에서 명시된 프로퍼티값은 변화하며, 명시되지 않으면 변하지 않는다.
만약, setState() 가 끝난 뒤에 특정 작업을 하고 싶으면 두번째 매개변수로 콜백함수를 등록하여 작업을 처리할 수 있다.
import React, { Component } from "react";
class Counter extends Component {
this.state = { //바로 선
number: 0,
fixedNumber: 0
};
render() { ... }
}
export default Counter;
React v16 부터는 위의 예시처럼 굳이 constructor을 쓰지 않더라도 state 값을 설정할 수 있다.
💡 constructor
constructor 메서드는 반드시 부모의 생성자 super() 를 명시적으로 호출해야 한다. 위의 예시의 경우 이를 통해 매개변수로 받은 값을 클래스의 props 프로퍼티에 설정할 수 있다.
prevState
...
onClick={() => {
this.setState({ number: number + 1 });
this.setState({ number: this.state.number + 1 }); // 두번써도 한번만 업데이트
}}
...
//
...
onClick={() => {
this.setState((prevState, props) => {
return { number: prevState.number + 1};
});
this.setState(prevState => ({ //prevState만 사용할경우 props 생략가능
number: prevState.number + 1 // 바로 값을 반환하므로 return 생략
}));
}}
...
위의 첫번째 예시처럼 한번의 이벤트에서 this.setState를 여러번 사용해서 연쇄적으로 number를 증가시킨다고 가정해보자. 예측된 결과는 클릭할 때 마다 2씩 증가하는 것이지만, 실제로는 1씩만 증가한다. React에서 state 변화는 비동기적으로 일어나며, 최적화를 위해 한번에 batch 처리해서 업데이트하기 때문이다.
이를 해결하기 위해서는 setState의 첫번째 콜백함수의 매개변수인 prevState 를 사용한다. 말 그대로 “이전값”을 의미하는 prevState는, setState가 호출되는 시점에서 계산된 state 값의 스냅샷(snapshot)으로, 이를 통해 state 업데이트의 비동기성 문제를 해결할 수 있다.
두번째 매개변수인 props는 this.props로 접근할 수 있어서 잘 쓰지않는다.
함수형 컴포넌트
React 16.8 전에는 함수형 컴포넌트에선 state를 사용할 수 없었으나, 이후에 useState라는 Hooks이 생기면서 state를 관리할 수 있게 되었다.
import React, { useState } from "react";
const Counter = () => {
const [number, setNumber] = useState(0);
const [fixedNumber, setFixedNumber] = useState(0);
return (
<div>
<h1>{number}</h1>
<h2>바뀌지 않음: {fixedNumber}</h2>
<button
onClick={() => {
// this.setState({ number: number + 1 });
setNumber(number + 1);
// setNumber(prevNumber => prevNumber+1);
}}
>
+1
</button>
</div>
);
};
export default Counter;
useState함수의 인자에는 state의 초깃값을 넣어주며, 클래스형 컴포넌트와는 달리 굳이 객체가 아니어도 상관없다. 이 함수를 호출하면 배열이 반환되는데, 첫번째 원소는 state 변수이며, 두번째 원소는 상태를 바꿔주는 세터(setter) 함수이다.변수와 세터함수의 이름은 정해지지 않고 배열 비구조화 할당을 통해 자유롭게 설정할 수 있지만, 일반적으로 세터함수의 이름은 set+변수이름을 쓰는것이 관례이다.
💡 배열 비구조화 할당
ES6부터 생긴 문법으로, 더욱 깔끔하게 배열 값을 할당할 수 있다.
const array = [1,2]; // 기존 문법 const one = array[0]; const two = array[1]; // 비구조화 할당 const [one, two] = array;
세터함수의 인자를 매개변수 값을 받는 콜백함수로 등록하면, 이 매개변수를 클래스컴포넌트의 prevState처럼 활용할 수 있다.
주의사항
const [number, setNumber] = useState({a: 1, b: 2);
...
setNumber( {...number, b:3} ); //spread 연산
state를 변경할 때는 반드시 setState나 useState의 세터함수를 이용해야 한다. 포인트 연산자로 접근해서 직접 변경시에는 경고가 발생한다. 만약 배열이나 객체와 같이 내부의 값을 변경해야 할때는 사본을 만들고 업데이트 한 뒤, 사본을 setState나 세터함수로 업데이트 한다.
'Web > React' 카테고리의 다른 글
[React] 5-1. ref (1) | 2023.12.04 |
---|---|
[React] 4-1 이벤트 (1) | 2023.12.02 |
[React] 3-1. 컴포넌트와 props (0) | 2023.11.27 |
[React] 2-2. JSX (2) | 2023.11.27 |
[React] 2-1. React의 기본구조 (2) | 2023.11.27 |