useState
2023-08-28
ReactHooks
Table of Contents
useState가 나오게 된 배경
- 클래스형 컴포넌트에서 함수형 컴포넌트로 바뀔 때 클래스형 컴포넌트의 강점인 생명주기(라이프사이클)과 상태값 관리를 Hook이란 개념으로 도입하게 되면서 상태값 관리를 useState라는 Hook으로 관리하게 되었습니다.
1. 프로젝트 세팅 React + TS + Vite
$ npm init vite@latest React-UseState -- --template react-ts
$ cd React-UseState
$ npm i
2. useState 사용해보기
진부한 Counter 예제를 만들어보겠습니다.
/src/App.tsx
import { useState } from "react";
import './App.css';
const App = () => {
const [count, setCount] = useState<number>(0);
function onClickHandler(event: React.MouseEvent) {
event.preventDefault(); // event 동작 방지
setCount(count + 1);
}
console.log("App 컴포넌트 재실행");
return (
<div>
<button onClick={onClickHandler}>count is {count}</button>
</div>
);
}
export default App;
- useState는 두 개의 항목이 포함된 배열을 리턴합니다. 첫번째 항목은 값을 리턴하고, 두번째 항목은 첫번째 항목을 변경할 set함수를 리턴하게 됩니다.
- set함수의 네이밍컨벤션은 set~~~이고, 활용방법은 set(업데이트 할값)입니다.
동작 순서
- 버튼을 누를때마다 onClickHandler가 실행
- 실행 문 안의 setCount가 실행되면
- 버튼 안에 있는 숫자 count가 하나씩 증가합니다.
의문점
- setCount가 실행되면 App 컴포넌트 재실행이라는 console.log가 찍히게 됩니다. 함수가 재실행된다는 의미입니다. 왜 재실행 될까요? set 함수는 렌더링을 트리거 할 수 있습니다.
useState에 값을 할당하는 방법
- 초기화
const [count, setCount] = useState(0);
- Lazy initialization(게으른 초기화)
import React, { useState } from "react";
import "./App.css";
const App = () => {
const [count, setCount] = useState<number[]>(
new Array(2000).fill(0).map((value, index) => {
console.log(value);
return value;
})
);
const [anotherCount, setAnotherCount] = useState<number>(0);
function onClickHandler(event: React.MouseEvent) {
event.preventDefault();
setAnotherCount((x) => x + 1);
}
console.log("App 컴포넌트 재실행");
return (
<div className='card'>
<button onClick={onClickHandler}>anotherCount is {anotherCount}</button>
</div>
);
};
export default App;
- 버튼을 누르면 anotherCount가 업데이트 되어 count의 값이 재 할당됩니다.
- 2000번의 배열을 돌고 나서 map에게 반환된 배열이 count에 할당됩니다.
적용 후
import React, { useState } from "react";
import "./App.css";
const App = () => {
const [count, setCount] = useState<number[]>(() => {
return new Array(2000).fill(0).map((value, index) => {
console.log(value);
return value;
});
});
const [anotherCount, setAnotherCount] = useState<number>(0);
function onClickHandler(event: React.MouseEvent) {
event.preventDefault();
setAnotherCount((x) => x + 1);
}
console.log("App 컴포넌트 재실행");
return (
<div className='card'>
<button onClick={onClickHandler}>anotherCount is {anotherCount}</button>
</div>
);
};
export default App;
- state가 처음 만들어 질 때만 실행됩니다.
- '비싼 비용의 계산' 이 필요할 때 써야합니다. 단순히 값을 넘기는 비용보다 함수를 쓰기 때문에 값의 크기가 크기 때문입니다.
localStorage의 접근,map,filter,find등의 배열을 조작하는 것들이 그 예가 될 수 있습니다. 일반적으로 함수를 통해서 값을 구해야한다면, 이는 비싼 비용이 드는 계산이며 게으른 초기화를 하는게 좋을 수도 있습니다.new Date()도 해주면 좋습니다.
useState의 값을 업데이트 하는 방법
- set 함수에 값을 전달하는 방식에 따라 업데이트 하는 방식이 조금 다릅니다.
function onClickHandler(event: React.MouseEvent) {
event.preventDefault();
setCount(count + 1); // setCount(0 + 1);
setCount(count + 1); // setCount(0 + 1);
setCount(count + 1); // setCount(0 + 1);
}
- onClickHandler에 setCount로 count의 값을 next state로 넣어줍니다. 의도는 count의 값을 3 증가시키는거였죠.
- 하지만 어떤가요? 버튼을 누른 후 버튼 안에 찍힌 값은 3이 증가된 값이 아닌, 1이 증가되었습니다.
- 함수 안에서 count의 값은 업데이트 되지 않은 값이니 업데이트 되지 않은 값에 1이 증가됩니다.
- 리액트의 state update 방식은 batch update로 여러개의 setState를 하나의 re-render로 묶습니다.
함수형 업데이트
function onClickHandler(event: React.MouseEvent) {
event.preventDefault();
setCount((prev) => prev + 1); // setCount(0 + 1);
setCount((prev) => prev + 1); // setCount(1 + 1);
setCount((prev) => prev + 1); // setCount(2 + 1);
}
- set 함수에 함수를 넣어주게 되면 이전 값을 가져오게 됩니다.
- 함수형 업데이트의 prev 값을 처음 불러온다면 렌더링 하기 전 할당된 값이며, 함수형 업데이트로 값을 업데이트를 한다면 prev로 저장된 값을 batch update로 넘겨질 값으로 업데이트 할 수 있습니다.
3. useState의 타입
-
useState에 마우스를 올려놓고 command + 마우스클릭을 해봅시다.
-
React가 useState를 정의하는 타입이 나오게 됩니다.
해석
function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
- useState는 S라는 제네릭 타입을 받고 있고, 파라미터로 S의 타입을 가지고 있는 값을 받습니다. 혹은 함수형태로 S타입의 값을 할당할 수 있습니다.
- useState는 두개의 타입을 리턴합니다. S의 타입을 가지는 초기값과
Dispatch<SetStateAction<S>>라는 함수를 리턴합니다.
Dispatch
-
다시 마우스를 놓고, Dispatch 타입을 따라가보면
-
Dispatch는 함수형입니다. A 타입의 value라는 파라미터를 받고, 아무것도 리턴하지 않습니다. 요약하면 A라는 타입의 value로 값을 받을 수 있는 함수입니다.
SetStateAction이 A 타입에 들어갔으니, SetStateAction이란 타입도 알아보겠습니다.
- SetStateAction 타입은 S 타입의 값을 리턴하게 되고, prevState. 즉, 전에 할당된 S타입의 값을 파라미터로 받고 S타입의 값을 리턴하고 있습니다.
Fiber : https://github.com/facebook/react/blob/v17.0.2/packages/react-reconciler/src/ReactFiberHooks.new.js
https://immigration9.github.io/react/2021/06/12/automatic-batching-react.html