준영이의 성장일기(FrontEnd)
React 공식문서 도장깨기(1) - useState 본문
리액트 공식문서를 주에 3회, 적어도 2회는 정리하는 시간을 가지자라고 개인적인 목표를 세웠다. 프로젝트를 하면서 리액트에 대해서 다시 복습 겸 좀 더 공부해야하는 필요성을 느꼈고 필요성을 느낀 순간부터 바로 목표를 세우게 되었다. 참조 레퍼런스는 리액트 공식문서 이고 다음과 같다.
https://ko.react.dev/reference/react
리액트(React) 참조 개요 – React
The library for web and native user interfaces
ko.react.dev
<useState>
개념: useState는 컴포넌트에 state 변수를 추가할 수 있는 React Hook이다.
✅ useState는 정확히 두 개의 값을 가진 배열을 반환한다. 차례대로 현재 state 그리고 상태를 업데이트하고 리렌더링을 촉발하는 set 함수로 구성되어 있다.
✅ useState는 일종의 Hook 이므로 컴포넌트의 최상위 레벨이나 직접 만든 Hook에서만 호출할 수 있으며 반복문/조건문에서 또한 호출할 수 없다.
<출처: 리액트 공식문서>
✅ initialState인 28은 초기 렌더링에만 사용되고 이후에는 무시된다.
const [age, setAge] = useState(28);
✅ set 함수
setName('junyoung')에서 junyoung은 nextState를 의미한다. 즉 이전의 state를 다음 state로 업데이트 해주는 것이다. nextState로 함수 또한 전달할 수 있는데 특징은 다음과 같다.
- 함수 전달: 상태 업데이트 시 함수를 nextState로 전달하면, React는 이 함수를 업데이터 함수로 취급한다.
- 업데이터 함수의 특성:
- 이 함수는 순수 함수여야 한다.(동일한 결과를 반환해야 한다)
- 이 함수는 대기 중인 이전 state를 유일한 인수로 사용해야 한다.
- 이 함수는 다음 state를 반환해야 한다.
- 업데이터 함수 대기열: React는 이 업데이터 함수를 대기열에 넣는다.
- 컴포넌트 리렌더링: React는 컴포넌트를 리렌더링한다.
- 대기열 처리: 다음 렌더링 중에, React는 대기열에 있는 모든 업데이터 함수를 적용하여 다음 state를 계산한다.
<중요>
❗ 다음 렌더링에 대한 state 변수만 업데이트한다. set 함수를 호출한 후에도 state 변수에는 여전히 호출 전 화면에 있던 이전 값이 담겨있기 때문에 console을 출력하면 호출 전 상태가 출력된다. setName('junyoung')으로 업데이트 하기 전 상태가 만약 junyoung2라면 console을 찍을 경우 junyoung2가 출력된다. junyoung인걸 확인할려면 return문에서 {name}을 통해서 확인할 수 있다.
const [name, setName] = useState("junyoung2")
function handleClick() {
setName('junyoung');
console.log(name); // 아직 "junyoung2"
}
return (
<div>
{name} //junyoung 출력
</div>
)
import { useState } from "react";
import "./App.css";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
function App() {
const [count, setCount] = useState(0);
const [name, setName] = useState("junyoung2");
function handleClick() {
setName("junyoung");
console.log(name); // 아직 "junyoung2"
}
return (
<>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<img
src={reactLogo}
className="logo react"
alt="React logo"
onClick={handleClick}
/>
</div>
<h1>Vite + React + {name}</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
);
}
export default App;
❗ React는 state 업데이트를 batch 한다. 모든 이벤트 핸들러가 실행되고 set 함수를 호출한 후에 화면을 업데이트한다. 이렇게 하면 단일 이벤트 중에 여러 번 리렌더링 하는 것을 방지할 수 있는 장점이 있다. 식당에서 주문하는 과정을 예로 들 수 있는데 식당에서 주문을 할 때 웨이터는 주문이 끝날 때까지 기다렸다가 다음 테이블로 넘어간다. 이처럼 batching라고도 하는 이 동작은 여러 컴포넌트에서 나온 다수의 state 변수를 업데이트할 수 있기 때문에 성능 상 이점을 발생시킨다.
✅ 이전 state를 이용하여 state 업데이트 시키기
이 코드의 결과값은 어떻게 나올까? 위에서 나온 개념에 의거하면 쉽게 맞출 수 있다.
const [age, setAge] = useState(42)
function handleClick() {
setAge(age + 1);
setAge(age + 1);
setAge(age + 1);
}
set 함수를 호출해도 이미 실행 중인 코드에서 age state 변수가 업데이트되지 않기 때문에 43이 3번 나오게 된다. 이를 해결할려면 업데이터 함수를 전달하여 이전 state를 이어받아 다음 state로 바뀔 수 있도록 구현할 수 있다.
const [age, setAge] = useState(42)
function handleClick() {
setAge(a => age + 1);
setAge(a => age + 1);
setAge(a => age + 1);
}
✅ 초기 state 다시 생성하지 않기
React는 초기 state를 한 번 저장하고 다음 렌더링부터는 이를 무시한다. 함수의 경우에도 해당 원리를 적용할 수 있는데 useState의 초기값으로 초기화 함수를 설정해주면 된다. 함수를 useState에 전달하면 React는 초기화 중에만 함수를 호출
한다. (ex. useState(createInitialTodos)); -> 이를 지연 초기화 함수라고 한다.
즉, 이 함수의 반환값이 상태의 초기값으로 설정된다. 함수가 매번 실행되지 않고 초기 렌더링 시에만 실행되기 때문에 성능상 많은 이점을 가지고 있다. 만약 createInitialTodos()로 초기값을 설정한다면 다음과 같은 문제점이 있다.
- 즉시 실행: useState에 createInitialTodos()처럼 함수 호출을 전달하면, 이 함수가 즉시 실행되어 반환값이 useState의 초기값으로 설정된다.
- 매번 실행: 이 방식에서는 컴포넌트가 렌더링될 때마다 createInitialTodos() 함수가 실행되는데 이는 초기값을 계산하는 함수가 항상 호출된다는 의미이다.(성능 상에서 단점 존재)
✅ 리렌더링이 너무 많이 발생하는 경우
Too many re-renders. React limits the number of renders to prevent an infinite loop.
해당 경우는 첨부된 이미지에서 첫번째 경우에 속한다. 이 경우는 버튼이 렌더링 될 때 즉시 handleClick()이 호출하게 되는데 onClick 속성에 전달된 것은 함수 handleClick의 반환값이지 함수 자체가 아니기 때문에 클릭 시 아무런 동작도 발생하지 않고 리렌더링 에러가 발생한다.
또한 이런 경우도 존재한다.
✅ state를 변화하는 함수가 있다면 반복해서 리렌더링 하게 된다. setState는 콜백으로 다시 렌더링을 트리거하기 때문에 위에 서술한 에러가 발생한다. 이 경우 클릭했을 때만 상태가 변경되도록 화살표 함수를 사용해서 해결할 수 있다.
onClick = {() => setHeaderSet(true}
'프론트엔드 > React.js' 카테고리의 다른 글
React 공식문서 도장깨기(2)-2 : useEffect, useLayoutEffect (3) | 2024.08.27 |
---|---|
React 공식문서 도장깨기(2)-1 : useEffect (0) | 2024.08.26 |
React useEffect, 상태 업데이트의 비동기성 (2) | 2024.08.17 |
Rendered more hooks than during the previous render (0) | 2024.08.03 |
Open API를 활용한 여행일정 구상하는 프로젝트(4) (0) | 2024.06.27 |