this.state
, this.setState
의 기능을 수행함수형 컴포넌트의 간결성을 유지하면서 동시에 상태 관리가 가능함
여러 번 호출하여 여러 개의 상태를 관리할 수 있음, 이를 통해 서로 관련 있는 상태들을 그룹화하고 독립적인 상태 로직을 분리하여 재사용성을 높일 수 있음
테스트와 디버깅이 쉬움 (상태와 로직이 명확하게 분리되어 있기 때문)
import React, { useState } from 'react';
useState
는 초기 상태 값을 인자로 받고, 상태 변수와 해당 상태를 업데이트하는 변수를 배열로 반환function App() {
const [count, setCount] = useState(0);
// ...
}
const [count, setCount] = useState(0);
setCount(1);
const [text, setText] = useState('');
setText('Hello, World');
const [array, setArray] = useState([]);
// 배열로 상태 업데이트
setArray([1, 2, 3]);
// 배열에 요소 추가
setArray([...prevArray, 4]); // push
setArray([0, ...prevArray]); // unshift
// 요소를 추가할 때 위와 같이 하면 문제없이 동작하지만 권장하지 않는다.
// 비동기적으로 실행 되기 때문에 바로 업데이트가 되지 않는다. (주의할 점에서 설명)
// 여러번 상태 업데이트를 할 경우 아래와 같이 업데이트를 권장
setArray(prevArray => [...prevArray, 4]); // push
setArray(prevArray => [0, ...prevArray]); // unshift
const [object, setObject] = useState({});
// 객체로 상태 업데이트
setObject({ name: 'yoon', age: 29 });
// 객체의 프로퍼티 업데이트
setObject(prevObject => ({ ...prevObject, live: 'incheon' }) );
// Bad
const [count, setCount] = useState(0);
count = 1; // 직접 수정하면 안 됩니다.
// Good
setCount(1); // 상태 변경 함수를 사용하세요.
// bad
function App() {
const [count, setCount] = useState(0);
const onClick = () => {
setCount(count+1);
console.log(count) // +1 하기 전의 값을 가져옴
}
return <button onClick={onClick}>{count}</button>
}
// Good
function App() {
const [count, setCount] = useState(0);
const onClick = () => {
const nextCount = count+1;
setCount(nextCount);
console.log(nextCount) // +1 한 값을 콘솔로 봐야 함
}
return <button onClick={onClick}>{count}</button>
}
// Bad
setCount(count + 1);
setCount(count + 1); // 이전 변경이 반영되지 않을 수 있습니다.
// Good
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1); // 항상 최신 상태를 반영합니다.
// bad (무한 루프에 빠짐)
function App() {
const [count, setCount] = useState(0);
setCount(count => count+1);
return <h1>{count}</h1>
}
// 해결법 (이벤트 핸들러를 사용해서 처리를 진행)
function App() {
const [count, setCount] = useState(0);
const onClick = () => {
setCount(count => count+1);
}
return (
<>
<h1>{count}</h1>
<button onClick={onClick}>클릭</button>
</>
)
}
useState
의 초기값에 복잡하고 무거운 계산이 필요한 함수를 넣으면 컴포넌트가 렌더링될 때마다 해당 함수가 호출되어 성능에 문제를 일으킬 수 있음.// 성능에 문제가 생길 수 있음
const heavyWork = () => {
console.log('시간이 오래 걸리는 엄청 무거운 작업');
return { name: '홍길동', age: 20};
}
function App() {
const [user, setUser] = useState(heavyWork);
}
// 이런 식으로 사용할 경우 컴포넌트가 렌더링될 때마다 heavyWork() 함수가 호출됨.
useState
가 함수를 초기값으로 받을 수 있는 기능을 제공 (lazy initialization), 초기 상태가 계산 비용이 큰 경우에 유용하게 사용// 해결법
const heavyWork = () => {
console.log('시간이 오래 걸리는 엄청 무거운 작업');
return { name: '홍길동', age: 20};
}
function App() {
const [user, setUser] = useState(() => heavyWork());
}
useState
초기값에 콜백을 넣어줘서 heavyWork()
함수가 맨 처음 렌더링될 때만 호출하게 만듬