프로퍼티의 특징은 컴포넌트 내부에서 값을 바꿀 수 없다라는 특징이 있다. 그렇지만 우리가 사용하는 일반적인 웹은 댓글을 남기는 등의 값을 바꿔야 하는 경우가 무수히 많다. 그러한 경우에 state를 사용한다.
state는 '값을 저장하거나 변경할 수 있는 객체'로 보통 버튼을 클릭하거나 값을 입력하는 등의 이벤트와 함께 사용된다. 아래의 코드는 setTimeout() 함수를 통해 4초 후 state에 저장되어 있는 값을 변경하는 코드이다.
import React, { Component } from "react";
class StateExample extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
formData: "no data",
};
this.handleData = this.handleData.bind(this);
setTimeout(this.handleData, 4000);
}
handleData() {
const data = "new data";
const { formData } = this.state;
this.setState({
loading: false,
formData: data + formData,
});
console.log("loading값", this.state.loading);
}
render() {
return (
<div>
<span>로딩중: {String(this.state.loading)}</span>
<span>결과: {this.state.formData}</span>
</div>
);
}
}
export default StateExample;
위의 코드를 보면 constructor(props)안에서 컴포넌트에서 관리하려는 변수 state 초기값을 this.state에 객체 형태로 정의하였고, handleData() 함수에서 컴포넌트 특수 변수 this.state를 사용하여 state 값에 접근하여 this.setState()를 사용하여 state 값을 변경한다.
이런식으로 state를 사용할 수 있는데 state를 사용할 때에는 몇가지 주의항이 있다.
위에서 보듯 state에 저장되는 객체는 반드시 초기화해야 하는데 그렇지 않으면 내부 함수에서 state 값에 접근할 수 없다. 만약 적당한 초기값이 없다면 state에 빈 객체라도 넣어야 한다.(this.state = {};) 또한 state에 저장되는 객체의 값은 직접 변경하면 안된다.
state 값을 직접 변경하면 안 되는 이유는 render() 함수로 화면을 그려주는 시점은 리액트 엔진이 정하기 때문이다. 즉 state 값을 직접 변경하더라도 render() 함수는 새로 호출 되지 않는다. 하지만 setState() 함수를 호출하여 state 값을 바꾼다면 리액트 엔진이 자동으로 render() 함수를 호출하므로 화면에 변경된 state 값을 출력할 수 있다.
하지만 무조건 setState()만이 방법이 아니다. 출력 검증 작업 없이 함수가 호출될 때마다 새롭게 화면을 출력하고 싶다면 클래스 인스턴스 변수와 forceUpdate() 함수를 사용하면 된다.
아래의 코드를 보자
import React from "react";
class ForceUpdateExample extends React.Component {
constructor(props) {
super(props);
// 상태 정의
this.loading = true;
this.formData = "no data";
// 이후 콜백 함수를 다룰때 bind를 선언하는 부분에 대해 다룹니다
this.handleData = this.handleData.bind(this);
// 생성 후 4초 후에 handleData를 호출합니다.
setTimeout(this.handleData, 4000);
}
handleData() {
const data = "new data";
// 상태 변경
this.loading = false;
this.formData = data + this.formData;
this.forceUpdate();
}
render() {
return (
<div>
{/* 상태 데이터는 this.state로 접근 가능합니다. */}
<span>로딩중: {String(this.loading)}</span>
<span>결과: {this.formData}</span>
</div>
);
}
}
export default ForceUpdateExample;
코드를 보면 컴포넌트 내장 함수 forceUpdate()를 호출하여 강제로 화면을 새로고침한다. 단, 이 방법은 리액트 성능에 제약이 있으므로 매번 새롭게 화면을 출력해야 되는 경우가 아니라면 가급적 사용하지 않는 것이 좋다.
참고 : Do it! 리액트 프로그래밍 정석