state는 컴포넌트 내부에서 바뀔 수 있는 값을 의마한다.
props는 컴포넌트가 사용되는 과정에서 부모 컴포넌트가 설정하는 값이며 컴포넌트 자신은 해당 props를 읽기 전용으로만 사용할 수 있다.
props를 바꾸려면 부모 컴포넌트에서 바꿔주어야 한다.
state는 클래스형 컴포넌트가 지니고 있는 state와 함수형 컴포넌트의 useState가 있다.
클래스형 컴포넌트의 state
Counter.js파일을 src 디랙터리에 생성해보자.
import React, { Component } from ‘react‘;
class Counter extends Component {
constructor(props) {
super(props);
// state의 초깃값 설정하기
this.state = {
number: 0
};
}
render() {
const { number } = this.state; // state를 조회할 때는 this.state로 조회합니다.
return (
<div>
<h1>{number}</h1>
<button
// onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정합니다.
onClick={() => {
// this.setState를 사용하여 state에 새로운 값을 넣을 수 있습니다.
this.setState({ number: number + 1 });
}}
>
+1
</button>
</div>
);
}
}
export default Counter;
위의 파일에서 각 코드가 어떤 역할을 하는지 알아보자.
컴포넌트에 state를 설정할 때는 다음과 같이 constructor 메서드를 작성하여 설정한다.
constructor(props) {
super(props);
// state의 초깃값 설정하기
this.state = {
number: 0
};
}
이는 컴포넌트의 생성자 메서드인데, 클래스형 컴포넌트에서 constructor를 작성할 때는 반드시 super(props)를 호출해 주어야 한다. 이 함수가 호출되면 현재 클래스형 컴포넌트가 상속하고 있는 리액트의 Component 클래스가 지닌 생성자 함수를 호출해 준다.
그 다음에 this.state값에 초깃값을 설정해 주었다. 컴포넌트의 state는 객체 형식이어야 한다.
하단 코드도 확인해보자.
render() {
const { number } = this.state; // state를 조회할 때는 this.state로 조회합니다.
return (
<div>
<h1>{number}</h1>
<button
// onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정합니다.
onClick={() => {
// this.setState를 사용하여 state에 새로운 값을 넣을 수 있습니다.
this.setState({ number: number + 1 });
}}
>
+1
</button>
</div>
);
}
}
export default Counter;
render함수에서 현재 state를 조회할 때는 this.state를 조회하면 된다.
그리고 버튼 안에 onClick값을 props르 넣어주었는데, 버튼이 클릭될 때 호출시킬 함수를 설정할 수 있게 해준다.
이벤트로 설정할 함수를 넣어 줄 때는 화살표 함수 문법을 사용하여 넣어 주어야 한다. 함수 내부에서는 this.setState라는 값을 사용하였다. 이 함수가 state값을 바꿀 수 있도록 해준다.
app.js는 다음과 같다.
import React from "react";
import Counter from "./Counter";
const App = () => {
return <Counter />;
};
export default App;
state 객체 안에 여러 값이 있을 때
state객체 안에는 여러 값이 있을 수 있다.
Counter를 다음과 같이 바꾸어보자.
import React, { 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를 조회할 때는 this.state로 조회합니다.
return (
<div>
<h1>{number}</h1>
<button
//onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정합니다.
onClick={() => {
//this.setState를 사용하여 state에 새로운 값을 넣을 수 있습니다.
this.setState({ number: number + 1 });
}}
>
+1
</button>
</div>
);
}
}
export default Counter;
현재 state안에 다른 값을 또 추가하고, 버튼이 클릭될 때 fixedNumber값은 그대로 두고 number값만 바꿀 것이다.
this.setState는 인자로 전달되 객체 안의 값만 바꾸어 준다.
앞처럼 state의 초기값을 지정하기 위해 constuctor메서드를 선언할 수도 있지만, 또 다른 방식으로 state의 초긱값을 지정해 줄 수도 있다.
import React, { Component } from "react";
class Counter extends Component {
//state의 초깃값 설정하기
state = {
number: 0,
fixedNumber: 0,
};
}
render() {
const { number, fixedNumber } = this.state; //state를 조회할 때는 this.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;
this.setState에 객체 대신 함수 인자 전달하기
this.setState를 사용하여 state값을 업데이트 할 때는 상태가 비동기적으로 업데이트 된다.
onClick에 설정한 함수 내부에서 this.setState를 두 번 호출하면,
onClick={() => {
//this.setState를 사용하여 state에 새로운 값을 넣을 수 있습니다.
this.setState({ number: number + 1 });
this.setState({ number: this.state.number + 1 });
}}

this.setState를 두 번 사용하는 것임에도 불구하고 숫자가 1씩 더해진다.
this.setState를 사용한다고 해서 state값이 바로 바뀌지는 않기 때문이다.
이에 대한 해결책은 this.setState를 사용할 때 객체 대신에 함수를 인자로 넣어 주는 것이다.
this.setState((preState, props) => {
return{
//업데이트 하고 싶은 내용
}
})
preState는 기존 상태이고, props는 현재 지니고 있는 props이다. 생략이 가능하다.
따라서 코드는 다음과 같이 수정한다.
import React, { Component } from "react";
class Counter extends Component {
//state의 초깃값 설정하기
state = {
number: 0,
fixedNumber: 0,
};
render() {
const { number, fixedNumber } = this.state; //state를 조회할 때는 this.state로 조회합니다.
return (
<div>
<h1>{number}</h1>
<h2>바뀌지 않는 값 {fixedNumber}</h2>
<button
//onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정합니다.
onClick={() => {
//this.setState를 사용하여 state에 새로운 값을 넣을 수 있습니다.
this.setState((preState) => {
return {
//업데이트 하고 싶은 내용
number: preState.number + 1,
};
});
//아래와 같은 코드이다.
//return을 생략할 수도 있다.
this.setState((preState) => ({
number: preState.number + 1,
}));
}}
>
+1
</button>
</div>
);
}
}
export default Counter;
이제는 2씩 잘 올라간다.
this.setState가 끝난 후 특정 작업 실행하기
setState를 사용하여 값을 업데이트 하고 난 다음에 특정 작업을 하고 싶을 때는 setState의 두 번째 파라미터로 콜백함수를 등록하여 작업을 처리할 수 있다.
import React, { Component } from "react";
class Counter extends Component {
//state의 초깃값 설정하기
state = {
number: 0,
fixedNumber: 0,
};
render() {
const { number, fixedNumber } = this.state; //state를 조회할 때는 this.state로 조회합니다.
return (
<div>
<h1>{number}</h1>
<h2>바뀌지 않는 값 {fixedNumber}</h2>
<button
//onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정합니다.
onClick={() => {
//this.setState를 사용하여 state에 새로운 값을 넣을 수 있습니다.
this.setState(
{
number: number + 1,
},
() => {
console.log("방금 setState가 호출되었습니다.");
console.log(this.state);
}
);
}}
>
+1
</button>
</div>
);
}
}
export default Counter;

console창에 잘 호출된 것을 볼 수 있다.
함수형 컴포넌트에서 useState 사용하기
여기서 Hooks라는 개념이 등장한다.
배열 비구조화 할당
배열 비구조화 할당은 이전에 배운 객체 비구조화 할당과 비슷하다. 배열 안에 들어 있는 값을 쉽게 추출할 수 있도록 해주는 문법이다.
const array = [1,2];
const one = array[0];
const two = array[1];
//다음과 같이 바꿀 수 있다.
const array=[1,2];
const [one, two] = array;
새 컴포넌트를 만들어서 useState를 사용해 보자.
src 디렉터리에 Say.js라는 파일을 생성하고 다음 코드를 작성해보자.
import React, { 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에 상태의 초기값을 먼저 넣어준다. 클래스형에서는 스테이트의 초기값은 객체 형태였지만, useState에서는 반드시 객체가 아니어도 상관없다. 숫자, 문자, 객체, 배열 모두 가능하다.
함수를 호출하면 배열이 반환되는데, 첫 번째 원소는 현재 상태아고 두 번째 원소는 상태를 바꾸어주는 함수이다. 이 함수를 Setter함수라고 부른다.
배열 비구조화 할당을 통해 이름은 자유롭게 정해 줄 수 있다.
이제 App.js를 고쳐서 확인해보자.
import React from "react";
import Say from "./Say";
const App = () => {
return <Say />;
};
export default App;

잘 작동하는 것을 볼 수 있다.
한 컴포넌트에서 useState여러 번 사용하기
useState는 한 컴포넌트에서 여러 번 사용해도 상관 없다.
import React, { 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;