useState
Hook을 사용하여 함수 컴포넌트에서 state를 사용할 수 있음useState
를 호출하여 state 변수를 선언할 수 있음import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
useState
는 state의 초기 값를 인수로 전달 받음
useState
는 state 변수와 이 변수를 갱신시킬 수 있는 함수 setState를 반환함
React는 리렌더링할 때 state 변수를 기억하고, 가장 최신에 갱신된 값을 제공함
useEffect
나 useCallback
의 dependency 목록에서 setState가 생략됨컴포넌트 내에서 여러 state 변수를 사용할 수 있음
function ExampleWithManyStates() {
// 여러 개의 state를 선언할 수 있습니다!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
...
}
useState
의 두 번째 인자 setState에 값 대신 함수를 전달할 수 있음function Counter({initialCount}){
const [count, setCount] = useState(initialCount);
return (
<>
Count: {count}
{/* button : normal form*/}
<button onClick={() => setCount(initialCount)}>Reset</button>
{/* button : functional form*/}
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
</>
);
}
Note
useState
는 class 컴포넌트의setState
메서드와 달리 객체 상태를 자동적으로 합쳐주지 않음
- 해결 방법 1 : spread 문법을 사용하여 객체를 합쳐주어야 함
- 해결 방법 2 :
useReducer
를 사용함const [state, setState] = useState({}); setState(prevState => { // Object.assign을 대신 사용해도 됨 return {...prevState, ...updatedValues}; });
initialState
인수는 첫 렌더링 때만 사용되고, 그 이후 렌더링에서는 무시됨initialState
에 값 대신 함수를 전달할 수도 있음const [state, setState] = useState(() => {
const initialState = someExpansiveComputation(props);
return initialState;
});
Object.is
비교 알고리즘을 사용하여 이전state와 새 state를 비교함undefined
이거나 null
인 경우true
이거나 false
인 경우string
인 경우umber
이거나 NaN
인 경우object
인 경우useMemo
를 사용하여 최적화할 수 있음// Before React 18
// batching
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// React will only re-render once at the end
}
// no batching
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React will render twice, once for each state update
}, 1000);
createRoot
를 사용하면서, 업데이트들이 어디서 기원되었는지 상관없이 모든 업데이트들은 자동적으로 그룹화(batch)됨// After React 18
// batching
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// React will only re-render once at the end
}
// batching
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React will only re-render once at the end
}, 1000);
flushSync
를 사용할 수 있음import { flushSync } from 'react-dom';
function handleClick() {
flushSync(() => {
setCount(c => c + 1);
});
// React has updated the DOM by now
flushSync(() => {
setFlag(f => !f);
});
// React has updated the DOM by now
}
closure
개념을 사용함closure
: 함수가 lexical scope밖에서 실행되더라도, 함수는 함수가 정의된 lexical scope을 기억하고 접근할 수 있음function useState(initialValue) {
let _val = initialValue; // _val은 useState에 의해 생성된 지역 변수
function state() {
// state는 inner function으로 closure임
return _val;
}
function setState(newVal) {
_val = newVal; // _val을 노출시키지 않고 _val의 값을 설정함
}
return [state, setState]; // 외부에서 사용하기 위해서 state와 setState를 노출시킴
}
let [foo, setFoo] = useState(0);
console.log(foo()); // 0이 출렸됨 - initialValue
setFoo(1); // useState 함수의 lexical scope 안에 존재하는 지역변수 _val의 값을 1로 설정함
console.log(foo()); // 1이 출력됨 - newVal
useState
hook에서 2개의 inner function이 존재함 state
: 위에서 정의한 지역 변수 _val
을 반환하는 함수setState
: 지역 변수 _val
을 전달받은 매개변수 newVal
로 설정하는함수state
와 setState
는 내부 변수인 _val
에 접근하고 조작할 수 있음state
와 setState
는 useState
의 스코프에 대한 접근을 유지함closure
라고 부름function Counter() {
const [count, setCount] = useState(0);
return {
click: () => setCount(count() + 1),
render: () => console.log('render:', { count: count() })
};
}
const C = Counter();
C.render(); // render: { count: 0 }
C.click();
C.render(); // render: { count: 1 }
_val
) 타입이 함수가 아닌 변수이어야 함_val
을 함수로 감싸지 않고 그대로 노출시키면, 에러가 발생됨function useState(initialValue) {
var _val = initialValue;
function setState(newVal) {
_val = newVal
}
return [_val, setState]; // _val을 그대로 노출시킴
}
var [foo, setFoo] = useState(0)
console.log(foo) // 0이 출력됨
setFoo(1) // useState의 scope 안의 _val를 1로 설정함
console.log(foo) // 0이 출력됨
useState
의 반환값으로부터 foo
를 구조 분해 할당했을 때, foo
는 초기에 useState
를 호출했을 때의 _val
를 참조하고 이 값은 바뀌지 않음const MyReact = (function() {
let _val; // module scope에 _val을 둠
return {
render(Component) {
const Comp = Component();
Comp.render();
return Comp;
},
useState(initialValue) {
_val = _val || initialValue; // useState를 호출할 때마다 _val에 값이 할당됨
function setState(neVal) {
_val = newVal;
}
return [_val, setState];
}
}
})();
MyReact
가 인수로 받은 컴포넌트를 렌더링할 수 있고, 매번 내부 변수인 _val
에 값을 할당할 수 있게 함function Counter() {
const [count, setCount] = MyReact.useState(0);
return {
click: () => setCount(count + 1),
render: () => console.log('render:', { count });
};
}
let App;
App = MyReact.render(Counter); // render: { count: 0 }
App.click();
App = MyReact.render(Counter); // render: { count: 1 }