컴포넌트에 state 추가하기
컴포넌트의 최상위 레벨에서 useState를 호출하여 하나 이상의 state 변수를 선언한다.
import { useState } from 'react';
function MyComponent() {
const [age, setAge] = useState(42);
const [name, setName] = useState('Taylor');
// ...
배열 구조 분해를 사용하여 [something, setSomething]과 같은 state 변수의 이름을 지정하는 것이 관례이다.
useState는 state 변수는 처음에 제공한 초기 state이다.
그리고 상호작용에 반응하여 다른 값으로 변경할 수 있는 set함수이다.
화면의 내용을 업데이트하려면 다음 state로 set 함수를 호출해야 한다.
function handleClick() {
setName('Robin');
}
React는 다음 state를 저장하고 새로운 값으로 컴포넌트를 다시 렌더링한 후 UI를 업데이트합니다.
**주의사항
set 함수를 호출해도 이미 실행 중인 코드의 현재 state는 변경되지 않는다.
function handleClick() {
setName('Robin');
console.log(name); // Still "Taylor"!
// 아직 "Taylor"입니다!
}
set함수는 다음 렌더링에서 반환할 useState에만 영향을 준다.
useState 기본 예시
1. 카운터 (숫자)
예제에서 count state 변수는 숫자를 받고 버튼을 클릭하면 숫자가 증가한다.
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
You pressed me {count} times
</button>
);
}
2. 텍스트필드 (문자열)
예제에서 text state 변수는 문자열을 받습니다. input에 타이핑하면 handleChange는 input DOM 요소에서 최신 input 값을 읽고 setText를 호출하여 state를 업데이트합니다. 이렇게 하면 아래에 현재 text를 표시할 수 있다.
import { useState } from 'react';
export default function MyInput() {
const [text, setText] = useState('hello');
function handleChange(e) {
setText(e.target.value);
}
return (
<>
<input value={text} onChange={handleChange} />
<p>You typed: {text}</p>
<button onClick={() => setText('hello')}>
Reset
</button>
</>
);
}
3.체크박스 (불리언)
예제에서 liked state 변수는 불리언을 받는다. input을 클릭하면 setLiked는 체크박스가 선택되어 있는지 여부에 따라 liked state 변수를 업데이트한다. liked 변수는 체크박스 아래의 텍스트를 렌더링하는 데 사용된다.
import { useState } from 'react';
export default function MyCheckbox() {
const [liked, setLiked] = useState(true);
function handleChange(e) {
setLiked(e.target.checked);
}
return (
<>
<label>
<input
type="checkbox"
checked={liked}
onChange={handleChange}
/>
I liked this
</label>
<p>You {liked ? 'liked' : 'did not like'} this.</p>
</>
);
}
4. 폼(두 개의 변수)
동일한 컴포넌트에 두 개 이상의 state 변수를 선언할 수 있다. 각 state 변수는 완전히 독립적이다.
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>
</>
);
}
이전 state를 기반으로 state 업데이트하기
age가 42라고 가정하면 이 핸들러는 setAge(age + 1)를 세 번 호출한다.
function handleClick() {
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
}
그러나 클릭해보면 age는 45가 아닌 43이 된다. set함수를 호출해도 이미 실행 중인 코드에서 age state 변수는 업데이트 되지 않는다. 따라서 43이된다.
이 문제를 해결하려면 state 대신 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은 업데이터 함수이다. 이 함수는 대기 중인 state를 가져와서 다음 state를 계산한다.
React는 업데이터 함수를 큐에 넣는다. 그러면 다음 렌더링 중에 동일한 순서로 호출한다.
a => a + 1은 대기 중인 state로 42를 받고 다음 state로 43을 반환한다.
a => a + 1은 대기 중인 state로 43을 받고 다음 state로 44를 반환한다.
a => a + 1은 대기 중인 state로 44를 받고 다음 state로 45를 반환한다다.
대기 중인 다른 업데이트가 없으므로, React는 결국 45를 현재 state로 저장한다다.
관례상 대기 중인 state 인수의 이름을 age의 a와 같이 state 변수 이름의 첫 글자로 지정하는 것이 일반적이나 prevAge 또는 더 명확하다고 생각하는 다른 이름으로 지정해도 된다.
React는 개발 환경에서 순수한지 확인하기 위해 업데이터를 두 번 호출할 수 있다.
객체 및 배열 state 업데이트
state에는 객체와 배열도 넣을 수 있다. React에서 state는 읽기 전용으로 간주되므로 기존 객체를 변이하지 않고, 교체를 해야 한다. 예를 들어, state에 form 객체가 있는 경우 변이하면 안된다.
// 🚩 state 안에 있는 객체를 다음과 같이 변이하지 마세요:
form.firstName = 'Taylor';
// ✅ 새로운 객체로 state 교체합니다
setForm({
...form,
firstName: 'Taylor'
});
초기 state 다시 생성하지 않기
React는 초기 state를 한 번 저장하고 다음 렌더링부터는 이를 무시한다.
function TodoList() {
const [todos, setTodos] = useState(createInitialTodos());
// ...
createInitialTodos()의 결과는 초기 렌더링에만 사용되지만, 여전히 모든 렌더링에서 이 함수를 호출하게 된다. 이는 큰 배열을 생성하거나 값비싼 계산을 수행하는 경우 낭비가 될 수 있다.
이 문제를 해결하려면, 대신 이를 useState에 초기화 함수로 전달한다.
function TodoList() {
const [todos, setTodos] = useState(createInitialTodos);
// ...
함수를 호출한 결과인 createInitialTodos()가 아니라 함수 자체인 createInitialTodos를 전달하고 있다는 것에 주목해라. 함수를 useState에 전달하면 React는 초기화 중에만 함수를 호출한다.
개발 환경에서는 React가 초기화 함수가 순수한지 확인하기 위해 초기화 함수를 두 번 호출할 수 있다.
key로 state 재설정하기
목록을 렌더링할 때 key 속성을 자주 접하게 된다. 하지만 key 속성은 다른 용도로도 사용된다.
컴포넌트에 다른 key를 전달하여 컴포넌트의 state를 재설정할 수 있다. 이 예제에서는 Reset 버튼이 version state 변수를 변경하고, 이를 Form에 key로 전달한다. key가 변경되면 React는 Form 컴포넌트(및 그 모든 자식)를 처음부터 다시 생성하므로 state가 초기화된다.
이전 렌더링에서 얻은 정보 저장하기
보통은 이벤트 핸들러에서 state를 업데이트한다. 하지만 드물게 렌더링에 대한 응답으로 state를 조정해야 하는 경우도 있다. 예를 들어, props가 변경될 때 state 변수를 변경하고 싶을 수 있다. 대부분의 경우 이 기능은 필요없다.
필요한 값을 현재 props나 다른 state에서 모두 계산할 수 있는 경우, 중복되는 state를 모두 제거해야한다. 너무 자주 재계산하는 것이 걱정된다면, useMemo 훅을 사용하면 도움이 될 수 있다.
전체 컴포넌트 트리의 state를 재설정하려면 컴포넌트에 다른 key를 전달해야 한다.
가능하다면 이벤트 핸들러의 모든 관련 state를 업데이트한다.
이 중 어느 것에도 해당하지 않는 희귀한 경우에는, 컴포넌트가 렌더링되는 동안 set 함수를 호출하여 지금까지 렌더링된 값을 기반으로 state를 업데이트하는 데 사용할 수 있는 패턴이 있다.
다음은 그 예시이다. CountLabel 컴포넌트는 전달된 count props를 표시한다:
export default function CountLabel({ count }) {
return <h1>{count}</h1>
}
카운터가 마지막 변경 이후 증가 또는 감소했는지를 표시하고 싶다고 가정하면 count prop은 이를 알려주지 않으므로 이전 값을 추적해야 한다. 이를 추적하기 위해 prevCount state 변수를 추가한다. trend라는 또 다른 state 변수를 추가하여 count의 증가 또는 감소 여부를 추적해라. prevCount와 count를 비교해서, 같지 않은 경우 prevCount와 trend를 모두 업데이트한다. 이제 현재 count props와 마지막 렌더링 이후 count가 어떻게 변경되었는지 모두 표시할 수 있다.
'React-study > dil' 카테고리의 다른 글
[DIL] useContext (0) | 2024.05.29 |
---|---|
[DIL] useReducer(2) (0) | 2024.05.28 |
[DIL] useReducer(1) (0) | 2024.05.27 |
[DIL] useState(2) (0) | 2024.05.17 |
[DIL] useState(1) (0) | 2024.05.16 |