리액트는 절대 반복하지 않는다. 리액트는 응용프로그램이 처음 렌더링되었을 때 모든 과정(모든 컴포넌트를 지나 모든 함수들을 실행해서 화면에 그리는 것)을 실행하고 그 후에는 끝이다. 때문에 리액트에게 어떤것이 변경되었고, 특정 컴포넌트가 재평가되어야 한다고 말하는 방법이 필요하다. 그것이 바로 state라는 개념이다.
useState는 React에서 제공하는 훅(Hook) 중 하나로, 함수형 컴포넌트에서 상태(state)를 관리할 수 있게 해준다.
useState를 사용하여 상태를 선언하면, 리액트는 해당 상태를 컴포넌트 내에 저장하고 관리한다.
useState는 배열을 반환하는데, 첫 번째 요소는 현재 상태값
이고, 두 번째 요소는 상태를 업데이트하는 함수.
일반적으로 배열 구조 분해 할당을 사용하여 상태값과 상태를 업데이트하는 함수를 개별적으로 추출한다.
const [state, setState] = useState(initialValue);
state
: 현재 상태값을 나타내는 변수. 컴포넌트가 초기화되거나 상태가 업데이트될 때마다 해당 변수에 저장된 값이 변경됨.setState
: 상태를 업데이트하는 함수. 이 함수를 호출하면 상태값이 변경되고, 리액트는 해당 컴포넌트를 리렌더링하여 변경된 상태를 반영함.initialValue
: 상태의 초기값을 설정하는 매개변수. 컴포넌트가 처음으로 렌더링될 때 사용되며, 이후에는 무시됨.함수형 컴포넌트
: useState는 클래스형 컴포넌트에서는 사용할 수 없다.
단일 상태값
: useState는 단일 상태값을 관리하는 데 사용된다. 여러 개의 상태를 관리해야 할 경우, useState를 여러 번 호출하여 각각의 상태를 개별적으로 관리할 수 있다.
함수 내부에서 호출
: useState는 함수 내부에서 호출되어야 한다. 함수 컴포넌트의 최상위 레벨에서 useState를 호출하여 상태를 선언해야 한다. 조건문, 반복문, 중첩 함수 등에서는 호출할 수 없다.
컴포넌트 렌더링 시 사용
: useState는 컴포넌트의 렌더링 과정에서 사용된다. 즉, JSX 코드 내부에서 호출하거나 useEffect나 useCallback 등의 훅과 함께 사용할 수 있다.
상태값 변경이 필요한 경우
: 상태값이 변하지 않고 고정적인 경우에는 useState를 사용할 필요가 없다. 단순히 값을 읽기만 하는 경우에는 useState 대신에 props나 컴포넌트 외부에서 전달받은 값을 사용하면 된다.
import React from "react";
import Card from "../UI/Card";
import ExpenseDate from "./ExpenseDate";
import "./ExpenseItem.css";
const ExpenseItem = (props) => {
let title = props.title; // let을 사용한 이유는 재할당..
const clickHandler = () => {
title = "updated!";
};
return (
<Card className="expense-item">
<ExpenseDate date={props.date} amount={props.amount} />
<div className="expense-item__description">
<h2>{title}</h2>
<div className="expense-item__price">{props.amount}</div>
</div>
<button onClick={clickHandler}>change title</button>
</Card>
);
};
export default ExpenseItem;
위의 ExpenseItem 컴포넌트 함수는 처음에 렌더링된 후에 다시 호출되지 않는다.
왜냐면, 클릭했을 때나 변수가 바뀌었을 때는 이 컴포넌트 함수를 다시 실행하라고 트리거하지 않기 때문이다.
리액트에게 다시 실행하라고 말하려면 리액트 라이브러리에서 무언가를 import해야 한다.
import React from "react";
import React, { useState } from "react";
const [title, setTitle] = useState(props.title);
//const [현재 상태 값, 그것을 업데이트 하는 함수] = useState();
// ...
const ExpenseItem = (props) => {
const [title, setTitle] = useState(props.title);
const clickHandler = () => {
console.log(title);
setTitle("updated!!!"); //state를 update
};
// ...
state가 변할 떄, 이 컴포넌트 함수를 다시 호출하고 싶으면 state를 업데이트하는 함수를 호출하면 된다.
여기서는 title이 변해야 하니 설정해둔 setTitle 함수를 호출, 변경하고 싶은 문자열을 입력했다.
변화하는 데이터를 갖고 있는데, 그 변화하는 데이터가 사용자 인터페이스에 반영되어야 한다면 state가 필요하다.
리액트는 state가 바뀌면 그 state가 등록된 컴포넌트를 다시 호출하고 재평가한다. 😄
+
State는 여러 방법으로 업데이트 될 수 있다. ex) setTimeout();
+
항상 모든 상태를 문자열로 초기화(useState('')
). 이유: 입력에 대해 변경 이벤트를 수신할 때마다, value of input in always String. 심지어 숫자를 저장해도 그것은 문자열로 된 숫자이고, 날짜도 마찬가지다. 그래서 여기서는 항상 문자열을 얻기 때문에 항상 모든 상태를 문자열로 초기화한다.const [insertToggle, setInsertToggle] = useState(false);
// 1.
setInsertToggle(!insertToggle);
// 2.
setInsertToggle((prev) => !prev);
위 중에 2번으로 쓰는 게 더 좋은 이유!
setInsertToggle(!insertToggle)
와 setInsertToggle((prev) => !prev)
모두 동일한 동작을 수행하지만, 두 가지 방식의 차이가 있다.
useState의 비동기성: useState
는 비동기적으로 상태를 업데이트한다. 즉, setInsertToggle(!insertToggle)
와 같이 현재 상태에 의존하여 새로운 상태를 계산하는 경우, 이전 상태값을 직접 참조할 때 문제가 발생할 수 있다. 이는 React가 상태를 일관되게 업데이트하기 위해 여러 setState
호출을 일괄 처리하기 때문이다. 그래서 이전 상태값을 사용하려면 함수 형태로 전달해야 한다.
함수형 업데이트의 안정성: setInsertToggle((prev) => !prev)
와 같이 함수를 사용하는 경우, React는 항상 최신 상태값을 보장한다. 이 방식은 이전 상태값에 의존하는 경우에 특히 유용하다. prev
매개변수를 통해 항상 최신 상태값을 참조할 수 있기 때문이다.
따라서 함수 형태로 상태 업데이트 함수를 사용하는 것이 React 컴포넌트에서 상태 관리를 더 안전하게 할 수 있는 방법 중 하나이다.