리액트에서 state는 컴포넌트 내부에서 바뀔 수 있는 값을 의미한다. props는 컴포넌트가 사용되는 과정에서 부모 컴포넌트가 설정하는 값이며, 컴포넌트 자신은 해당 props를 읽기 전용으로만 사용할 수 있다. props를 바꾸려면 부모 컴포넌트에서 바꿔줘야한다.
클래스형 컴포넌트의 state
Counter.js
import React, { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = {
number: 0,
};
}
render() {
const { number } = this.state; // state를 조회할 때는 this.state로 조회
return (
<div>
<h1>{number}</h1>
{/* this.setState를 사용하여 state에 새로운 값을 넣을 수 있다.*/}
<button onClick={() => this.setState({ number: number + 1 })}>
+1
</button>
</div>
);
}
}
export default Counter;
컴포넌트에서 state를 설정할 때는 constuctor 메서드를 작성해야 한다.
constructor(props) {
super(props);
this.state = {
number: 0,
};
}
이는 컴포넌트의 생성자 메서드이다. 클래스형 컴포넌트에서 constuctor를 작성할 때는 반듯이 super(props)를 호출해 주어야 한다. 이 함수가 호출되면 현재 클래스형 컴포넌트가 상속하고 있는 리액트의 Component 클래스가 지닌 생성자 함수를 호출해 준다.
다음에는 this.state 값에 초깃값을 설정해 준다.
App.js
import { Component } from 'react';
import Counter from './Counter';
class App extends Component {
render() {
return (
<Counter />
);
}
}
export default App;
state의 초깃값을 constuctor 메서드 선언없이 설정하기
import React, { Component } from 'react';
class Counter extends Component {
state = {
number: 0,
};
render() {
const { number } = this.state;
return (
<div>
<h1>{number}</h1>
<button onClick={() => this.setState({ number: number + 1 })}>
+1
</button>
</div>
);
}
}
export default Counter;
this.setState에 함수 인자 전달
onClick={() => {
this.setState({ number: number + 1 });
this.setState({ number: this.state.number + 1 });
}}
처럼 작성하면 this.setState를 두 번 사용했지만 버튼을 클릭했을 때 숫자가 1만 더해진다.
setState는 비동기적으로 업데이트 되기 때문에 setState가 두번 호출되었다고 해서 값이 바로 바뀌지 않는다.
이를 해결하기 위하여 this.setState를 사용할 때 객체 대신 함수를 인자로 넣어 사용한다
this.setState((prevState, props) => {
return {
...
}
})
Counter.js
import React, { Component } from 'react';
class Counter extends Component {
state = {
number: 0,
};
render() {
const { number } = this.state;
return (
<div>
<h1>{number}</h1>
<button
onClick={() => {
this.setState((prevState) => {
return {
number: prevState.number + 1,
};
});
this.setState((prevState) => ({
number: prevState.number + 1,
}));
}}
>
+1
</button>
</div>
);
}
}
export default Counter;
this.setState 후 특정 작업 실행
setState를 사용하여 값을 업데이트한 후 특정 작업을 하고 싶다면 setState의 두 번째 파라미터로 콜백 함수를 등록하여 처리할 수 있다.
onClick={() => {
this.setState(
(prevState) => ({
number: prevState.number + 1,
}),
() => {
console.log('setState>>>' + this.state.number);
}
);
}}
함수 컴포넌트에서 useState 사용
리액트 v16.8 이전에는 함수 컴포넌트에서 state를 사용할 수 없었다. 이후 useState를 사용하여 함수 컴포넌트에서도 state를 사용할 수 있게 되었다.
useState
import React, { useState } from 'react';
const Login = () => {
const [message, setMessage] = useState('');
const onClickLogin = () => setMessage('로그인 되었습니다.');
const onClickLogout = () => setMessage('로그아웃 되었습니다.');
return (
<div>
<button onClick={onClickLogin}>로그인</button>
<button onClick={onClickLogout}>로그아웃</button>
<h1>{message}</h1>
</div>
);
};
export default Login;
useState 여러번 사용
import React, { useState } from 'react';
const Login = () => {
const [message, setMessage] = useState('');
const onClickLogin = () => setMessage('로그인 되었습니다.');
const onClickLogout = () => setMessage('로그아웃 되었습니다.');
const [color, setColor] = useState('black');
return (
<div>
<button onClick={onClickLogin}>로그인</button>
<button onClick={onClickLogout}>로그아웃</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 Login;
state 주의 사항
state값을 바꾸어야 할 때는 setState 또는 useState를 통해 전달받은 세터 함수를 사용해야 한다.
배열이나 객체를 업데이트 할 때는 배열이나 객체를 복사한 후 그 값을 변경하고 세터 함수를 통해 업데이트 해야한다.