
import { useState } from 'react';
const [data, setData] = useState('initialState');
useState 를 배우면서 알아야 할 가장 중요한 부분을 미리 정리합니다.
- useState 를 사용할 때 배열 구조 분해를 사용하여 변수의 이름을 짓는 것이 규칙
- useState() 안에 매개변수로 들어가는 것은 초기 상태로서 이 매개변수는 초기 렌더링 이후에는 무시
- 이 매개변수에는 함수도 들어갈 수 있는데 그 함수에는 return 값이 꼭 필요합니다.
- useState 는 배열 구조 분해를 할때 두개의 변수를 선언합니다.
- 하나는 데이터가 저장될 장소, 다른 하나는 데이터의 값을 내가 원하는 대로 바꿀 함수입니다.
- useState 는 루프문이나 조건문에서 사용할 수 없습니다.
내가 쓴 예시 코드
import { useState } from 'react';
export default function Form() {
const [name, setName] = useState('Taylor');
const [age, setAge] = useState(42);
return (
<>
<input
value={name}
onChange={e => setName(e.target.value)}
/>
<button onClick={() => setAge(age + 1)}>
Increment age
</button>
<p>Hello, {name}. You are {age}.</p>
</>
);
}
만약 이러한 코드가 있다면 React 에서는 어떻게 동작하게 될까요?
function handleClick() {
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
}
이 함수를 실행해도 나이는 45가 아닌 43만 되는데,
이것은 set 함수를 호출해도 이미 실행 중인 코드에서 나이 상태 변수를 업데이트하지 않기 때문입니다.
따라서 각 setAge(age + 1) 호출은 setAge(43)이 되는 것입니다.
이 문제를 해결하기 위해 다음 상태 대신에 setAge에 업데이터 함수를 전달할 수 있습니다:
function handleClick() {
setAge(a => a + 1); // setAge(42 => 43)
setAge(a => a + 1); // setAge(43 => 44)
setAge(a => a + 1); // setAge(44 => 45)
}
여기서 a => a + 1은 업데이터 함수입니다. 이는 대기 중인 상태를 받아서 다음 상태를 계산합니다.
React는 업데이터 함수를 대기열(stack)에 넣습니다.
이렇게 3번의 업데이트 함수가 대기열에 저장이 되면 최종적으로 마지막에 컴포넌트를 리렌더링 할때
stack 에 저장되어 있던 업데이트 들이 차례대로 실행되면서 3번의 리렌더링이 됩니다.
우리의 눈에는 보이지 않을 정도로 빠르게 리렌더링이 되기 때문에 한번에 바뀌는 모습을 볼 수 있습니다.
다른 대기 중인 업데이트가 없으므로 React는 최종적으로 45를 현재 상태로 저장합니다.
React에서는 객체와 배열을 상태(state)에 넣을 수 있습니다.
그러나 React에서 상태는 읽기 전용으로 간주되므로 기존 객체를 직접 변경(mutate)하는 대신,
해당 객체를 대체(replace)해야 합니다.
"읽기 전용(Read-only)"이란 말은 데이터를 가져오거나 읽을 수는 있지만, 데이터를 변경하거나 수정할 수 없는 것을 의미합니다. React에서 상태(state)는 한 번 설정된 후에는 변경되어서는 안 되는 것으로 간주됩니다. 이것은 React의 중요한 설계 원칙 중 하나인 "불변성(Immutability)"의 일부입니다.
// 이렇게 변경하지 마세요.
setData({ firstName: 'chan' })
// 이렇게 변경하세요.
setData({
...data,
firstName: 'chan'
})
이렇게 불변성을 유지해야 React 에서 상태 변경을 참고해서 변경된 부분만 바꾸고 리렌더링을 진행합니다.
만약 초기값을 함수를 넣는다면 이렇게 만들어야 합니다.
// 이렇게 하면 리렌더링시 매번 함수를 실행합니다.
const [data, setData] = useState(takeData())
// 이렇게 하면 초기 리렌더링일때만 실행합니다.
const [data, setData] = useState(takeData)