# [React]  Hook- useState

# ⭐들어가며⭐

React에는 여러가지 Hook이 존재한다. 리액트 프로젝트를 만들기 쉽게 만든 API의 일종이다. 리액트 Hook 중에서 가장 기본 및 기초가 되는 useState에 대해 알아보자.

[Built-in React Hooks – React](https://react.dev/reference/react/hooks)

# *useState*

공식문서 정의

→ **useState is a React Hook that lets you add a state variable to your component.**

### *원형*

```jsx
import {useState} from 'react';
const [state, setState] = useState(initialState);
```

`useState`는 2개의 return 값을 반환한다.

* `state`
    
    * 현재 state값, 이해를 위해 지금은 변수값이라고 생각하자. 해당 변수의 초기값으로는 `initialState` 값이 들어간다.
        
* `setState`
    
    * state를 update하는 함수이다.
        

→ 해당 구조는 Javascript의 구조분해할당 방식을 사용하여 useState의 리턴값을 저장해준 것이다.

[구조 분해 할당 - JavaScript | MDN](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)

---

# *❓처음 useState를 공부할때 3가지 의문이 들었다.*

### Q1. 왜 `let`을 사용하지 않고 `state`를 사용해서 변수를 저장하지?

### Q2. `setState`를 따로 만들어서 `state`를 관리하는 이유가 뭐지?

### Q3. `setState`함수에 함수를 넣어주는 이유는 무엇일까?

**해당 질문들을 답하기 위해서는 리액트의 랜더링 동작에 대해 이해해야 한다.**

먼저, Javascript처럼 코드를 작성해보자. 예상대로라면, `Count` 값이 버튼을 누를때마다 1씩 증가해야 한다.

```jsx
export default const Counter = () => {
    // Count 값 저장 변수
	let count = 0;
    // Count 증가 Event Handler
	const increaseCount= ()=> {
		count += 1;
		console.log(count);
	};

	return (
		<div>
			<button onClick={increaseCount}>+1 Button</button>			
			<p>Count : {count}</p>
		</div>
	);
};
```

&lt;결과&gt;

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1700632106780/6c26a896-06a3-4366-a024-72b28b7ed071.png align="center")

→ 아무리 눌러도 `Count` 값이 바뀌지 않음을 확인 할 수있다.

일반 변수인 `let` 으로 선언하여 이벤트를 처리하면, 값이 바뀌지 않는 이유는 리액트의 랜더링 방식에있다.

### #랜더링이란?

랜더링이란, 컴포넌트가 현재 `props`와 `state`의 상태에 기초하여 UI를 어떻게 구성할지 컴포넌트에게 요청하는 작업을 의미한다.

### #리액트의 랜더링 방식

리액트는 Declarative(선언적) Programming이다. 컴포넌트를 만들기 위해 복잡한 DOM Element에 접근하지 않고, 리액트 딴에서 자동으로 해준다.

### Q1. 왜 `let`을 사용하지 않고 `state`를 사용해서 변수를 저장하지?

### A1. **리액트는 초기에 전체 컴포넌트를 전체 실행시키고, 일반 변수가 바뀌었다고 해서 재렌더링하지 않는다.**

→⭐하지만, State값이 변경되면 이와 관련된 내용들에 ReRendering된다.⭐

이때, 관련된 내용들은 해당 state를 포함한 최상위 컴포넌트와 이의 모든 자식 컴포넌트를 의미한다.

결국, state를 통해 변수를 관리해야, UI에 바뀐 값이 보이는 것이다. 예제 코드를 살펴보자.

`import {useState} from "react";`

```jsx
export default const Counter = () => {
	const [count, setCount] = useState(0);

	const increaseCount= () => {
		setCount((prev)=> prev+1);
		console.log(count);
	};

	return (
		<div>
			<button onClick={increaseCount}>+1 Button</button>			
			<p>Count : {count}</p>
		</div>
	);
};
```

&lt;실행 결과&gt;

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1700632304488/bca61a02-1bdb-41e8-b4b5-0c3874c5a6c6.png align="center")

Count 값이 1씩 정상적으로 증가함을 볼 수 있다.

하지만! 콘솔창을 살펴보자. (총 3번 버튼을 눌렀다)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1700632353774/c17e37bb-b962-437d-8cfa-ea2f0895634d.png align="center")

**setCount로 값을 증가시키고 출력했음에도 증가한 값이 아닌 기존값이 출력됬음을 알 수 있다.**

왜 그런 것일까?

**원인은 리액트의 set함수 처리 방식에 있다.**

해당 그림은, 리액트 라이프 사이클을 표현한 그림이다.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1700632399087/17b49055-3cb2-4e2f-b1a4-51e99eb9270b.png align="center")

리액트는 리렌더링을 유발하는 동작들을 큐에 저장하여 실행한다.

* 리렌더링을 유발하는 동작
    
    * setState()
        
    * forceUpdate() (ReactDOM.render()과 같다고 할 수 있다.)
        
    * ***useState()의 setter(setState함수)***
        
    * useReducer()의 dispatchers
        

하지만, 해당 큐들에 저장하는 것이 해당 코드를 읽다가 만났을때 실행하는 동작이고, 실제로 실행되는 시점은 `getDereivedStateFromProps` 단계에 도달했을 때이다. 즉, render직전에 실행되는 것이다. (return 전에 실행된다고 보면 된다.)

다시 예제 코드를 살펴보자. `console.log` 는 setter 함수 이전에 실행되므로, 변경되지 않는 값이 들어간 것이다.

---

### Q2. `setState`를 따로 만들어서 `state`를 관리하는 이유가 뭐지?

### A2. state는 불변성(immmutable)을 유지해야하기 때문입니다.

컴포넌트는 현재의 this.state와 setState를 비교해서 업데이트가 필요한 경우에만 render 함수를 호출하는데, state를 직접 수정하면 리액트가 render 함수를 실행하지 않아서, 필요한 리랜더링이 발생하지 않을 수 있습니다. 상태 변경을 추적하기 위해서는 setter함수를 사용합시다!

---

---

### Q3. setState 함수에 함수를 넣어주는 이유는 무엇일까?

위의 예제 코드를 자세히 살펴보면, `setCount(count++)`이 아니라, `setCount((prev) ⇒ prev + 1)`로 작성되어 있음을 알 수 있다. 이렇게 함수를 넣어주는경우, 리액트에서 이전값을 자동으로 참조한다.

해당 예제를 실행해보면 이해하기 쉽다.

```jsx
export default const Counter = () => {
	const [count, setCount] = useState(0);

	const increaseCount = () => {
      setCount(count + 1);
      setCount(count + 1);
      setCount(count + 1);
  };

	return (
		<div>
			<button onClick={increaseCount}>+3 Button</button>			
			<p>Count : {count}</p>
		</div>
	);
};
```

`<실행결과> (버튼 3번 클릭)`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1700632491573/ec654737-5166-43ed-9db6-1ef2c4b26733.png align="center")

→ `Count : 3`이 아닌 이유는 3개의 setCount 모두에 count 값이 0 (초기값)으로 들어갔기 때문이다.

이전에, set함수들은 큐에 저장된다고 하였는데, 이때 현재 count 값이 인자로 저장된 것이다. 그래서 모두 0으로 저장되었고 이를 실행한 것이다. 하지만, 우리가 원하는 값은 Count : 3이 되는것이 아닌가? 이를 위해서, setter 함수에 함수를 넣어주면 이전값을 참조하게끔 리액트는 useState를 설계하였다.

### **A3. setState 함수 내에서 이전 state값을 참조하기 위해**

→ 정상적으로 실행시키기 위해서는 setState 함수에 익명함수를 넣어주면 된다.

```jsx
export default const Counter = () => {
	const [count, setCount] = useState(0);

	const increaseCount = () => {
      setCount((prev) => prev + 1);
      setCount((prev) => prev + 1);
      setCount((prev) => prev + 1);
  };

	return (
		<div>
			<button onClick={increaseCount}>+3 Button</button>			
			<p>Count : {count}</p>
		</div>
	);
};
```

해당 방식으로 작성하면, 정상적으로 Count : 3이 UI에 렌더링됨을 확인할 수 있다.

# ✏️마치며

리액트에서 가장 중요한 주제가 컴포넌트와 상태라고 생각한다. 그만큼, 자주 보게되고 자주 사용할 테니 확실하게 익혀두는게 좋을 것 같다.

edited by 김지호
