useState
는 React Hook으로 Component에 상태변수(State Variable)을 추가할 수 있도록 해준다.
const [state, setState] = useState(initialState)
Component는 리액트의 핵심 개념 중 하나로 User Interface를 구축하는 기반으로 독립적이고 재사용이 가능한 코드 단위를 말한다.
(Independent and reusable bits of code)
시간, 상태에 따라 변경되는 데이터를 관리하는 변수로 일반적인 변수와 다르게 re-render를 trigger한다.
useState(initialState)
useState는 컴포넌트의 최상단에서 상태변수를 declare하기 위해 호출한다.
import { useState } from 'react';
function MyComponent() {
const [age, setAge] = useState(28);
const [name, setName] = useState('Taylor');
const [todos, setTodos] = useState(() => createTodos());
// ...
또한 상태변수의 이름을 짓는 컨벤션은 배열을 사용해 [something, setSomething]
처럼 한다.
initialState
: 초기상태일때의 원하는 값, 어떠한 형태의 타입도 가능하나 함수의 경우 조금 다름. 그리고 초기 렌더링 후에는 무시됨. (This argument is ignored after the inital render.)useState
는 정확히 2개의 값이 들어있는 배열을 반환한다.
Set Function
상태를 다른 값으로 업데이트하고 동시에 re-render를 trigger 한다.useState
는 Hook이기 때문에 오직 컴포넌트 상단 혹은 직접 만든 Hook의 상단에서 호출할 수 있다.
또한 반복문과 조건문 내부에서는 호출할 수 없다. 만약 이런 경우가 필요하다면 새로운 컴포넌트를 만들어서 상태를 옮겨야 한다.
Strick Mode 에서는 Initiallizer함수가 투번 호출되는데, 이는 혹시모를 사고(에러 등등이겠지?)를 찾는데 도와주기 위함이고, 개발모드에서만 이루어진다.
set
functions, like setSomething(nextState)
set
함수는 useState
에 의해서 반횐되는데, 이는 새로운 상태 값을 업데이트하고 component를 re-render하도록 trigger하는 역할을 한다.
또한 next state, 즉 업데이트가 될 다음 상태값을 직접 넣어줄수도 있고 아니면 이전의 상태(previous state)를 이용해 계산하는 함수를 넣어줄수도 있다.
const [name, setName] = useState('Edward');
function handleClick() {
setName('Taylor');
setAge(a => a + 1);
// ...
nextState
: 새로운 상태가 되었으면 하는 값. 어떤 형태의 변수도 가능하나 함수의 경우 조금 다르게 동작한다.set
함수는 반환값이 없다.
set
함수는 오직 next render에 대한 상태변수(state variable)만 업데이트 한다set
함수를 호출한 다음 상태변수(state variable)를 읽으면 호출하기 전의 화면의 old value를 받는다set
함수로 넘긴 새로운 상태값이 이전과 비교해 차이가 없으면 re-render는 건너뛴다 (최적화)set
함수를 호출하는 것은 현재 렌더링되는 컴포넌트 내에서만 가능하다import { useState } from 'react';
function MyComponent() {
const [age, setAge] = useState(42);
const [name, setName] = useState('Taylor');
// ...
useState
Hook을 컴포넌트 상단에 호출하고, 컨벤션에 맞게 상태변수와, set 함수의 이름을 지어준다.
위에서 정리한것과 같이 useState
는 2개의 값을 가지고 있는 배열을 반환하는데,
아래와 같이 set
함수를 호출해 상태변수를 업데이트 할 수 있다.
React는 다음상태(여기선 Robin)를 저장하고, 컴포넌트를 다시 렌더링해 UI를 업데이트 한다.
🧨 여기서 중요한건 set
함수를 호출해도, 이미 실행 중인 코드의 현재 상태는 변경되지 않는다!
오직 다음 렌더링부터 useState
가 반환할 내용에만 영향을 준다.
function handleClick() {
setName('Robin');
console.log(name); // Still "Taylor"!
}
아래와 같이 age를 42라하고 setAge(age + 1)
를 3번 연달아서 호출한다고 가정해보자
function handleClick() {
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
}
어떤 버튼을 클릭해 handleClick
함수가 호출되어도 age는 45가 아니라 43이다 :) ㅋㅋ
왜냐하면 set
함수를 호출해도 이미 실행중인 코드의age
의 상태는 업데이트 되지 않았기 때문이다.
따라서 각각의 setAge(age + 1)
는 setAge(43)
이 된다.
이 문제(?)를 해결하기 위해서 next state대신 updater function을 사용할 수 있다.
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
이게 updater function이다.
pendting state를 가지고 와서 next stata를 계산한다.
리랙트는 update function을 queue에 put하고 next render가 될동안 set 함수가 호출 될 거임
a => a + 1
는 pending state로 42를 받고 43을 next state로 반환a => a + 1
는 pending state로 43를 받고 44을 next state로 반환a => a + 1
는 pending state로 44를 받고 45을 next state로 반환queue에 update해야할 task가 더이상 없어, React는 45를 current state로 저장하고 끝냄.
form이라는 object가 state안에 있을때 새로운 상태로 업데이트를 할때 아래처럼 해야한다.
잘못된 예
form.firstName = "Taylor";
옳바른 예
setForm({
...form,
firstName: "Taylor"
});
function handleClick() {
console.log(count); // 0
setCount(count + 1); // Request a re-render with 1
console.log(count); // Still 0!
setTimeout(() => {
console.log(count); // Also 0!
}, 5000);
}
왜냐하면 state는 snapshot처럼 동작하기 때문인데, state를 update하면 새로운 state 값으로 또 다른 rendering이 요청되지만, 이미 실행중인 이벤트 핸들러의 Javascript 변수 개수에 영향을 주지 않는다.