Search

useState

리액트 16.8 버전 이후로, 함수형 컴포넌트에서 상태(state)를 사용할 수 있게 해주는 React Hooks가 도입되었습니다. useState()는 리액트 Hook 중에서도 가장 기본적인 함수로, 함수형 컴포넌트에서 상태의 생성과 변경과 같은 기본적인 상태 관리를 useState()를 호출해서 사용합니다.
상태 관리(state management)에 대해 잘 모르신다면 리액트 시작하기 상태(state) 관리편에서 확인하세요.

useState 사용 방법

useState()를 비롯한 리액트 Hook은 사용하기 전에 먼저 react 라이브러리에서 관련 함수를 임포트해야 합니다.
import { useState } from 'react';
JavaScript
복사
useState()를 사용하려면 함수형 컴포넌트의 범위 내에서 아래 코드처럼 호출하면 됩니다.
const [state, setState] = useState();
JavaScript
복사
useState()를 호출하면 상태관리에 사용되는 두개의 값을 가진 배열을 리턴하는데, 위 코드처럼 구조분해할당을 적용해서 사용하는 것이 일반적인 사용 방법입니다. 첫번째 값인 state가 상태화 된 값이고 두번째 값인 setState는 상태를 변경하는 함수입니다. 두번째 값인 setState의 경우 상태를 변경하는 함수라는 것을 명시적으로 나타내기 위해 명칭의 앞에 “set”을 붙여주도록 합시다.
예를 들어 만약 컬러 값을 상태로 사용한다고 가정하면 아래와 같은 명칭으로 선언해서 사용하는 식입니다.
const [color, setColor] = useState();
JavaScript
복사
useState()를 호출할때 인자에 값을 지정하면 반환된 상태의 초기 값이 됩니다. 아무 값도 지정하지 않으면 초기 값은 undefined가 됩니다.
const [color, setColor] = useState('blue'); console.log(color); // blue
JavaScript
복사
상태 값의 변경은 상태와 함께 선언된 set 함수를 사용해서 변경해야만 합니다. 아래처럼 상태를 const 타입으로 선언했을때 상태를 직접적으로 변경 하려고 하면 Uncaught TypeError: Assignment to constant variable. 에러가 발생하게 됩니다.
const [color, setColor] = useState('blue'); setColor('red'); // 🙆🏻‍♂️ color 상태를 red로 변경 color = 'red'; // 🙅🏻‍♂️ Uncaught TypeError 발생
JavaScript
복사
setColor()를 사용해 상태를 변경해야 리액트가 이를 인지하고 해당 컴포넌트를 다시 랜더링해 변경된 상태를 화면에 적용합니다.
만약 상태를 let 또는 var 타입으로 선언하게 되면 값의 직접적인 변경은 가능하지만 재 랜더링을 수행하지 않으므로 화면에 변경된 값이 적용되지 않습니다. 이런 경우에는 에러가 따로 발생하지 않기 때문에 문제 파악이 어려워 질 수 있는데요. 상태의 직접적인 변경을 사전에 방지하기 위해 useState()는 꼭 const 타입으로만 사용하도록 합시다.
set 함수를 사용해서 상태를 업데이트해야 컴포넌트가 상태 값의 변경과 함께 랜더링을 다시 수행하면서 변경된 상태 값을 화면에 정상적으로 반영하게 됩니다.
import { useState } from 'react'; import ReactDOM from 'react-dom/client'; const App = () => { // useState()를 사용해 초기 값이 blue인 상태 생성 const [color, setColor] = useState('blue'); return ( <> <h1 style={{ color }}>현재 색상: {color}</h1> {/* setColor 함수로 color 상태를 red로 변경 */} <button onClick={() => setColor('red')}> 빨간색으로 </button> {/* setColor 함수로 color 상태를 blue로 변경 */} <button onClick={() => setColor('blue')}> 파란색으로 </button> </> ); }; // App 컴포넌트 랜더 const rootNode = document.getElementById('root'); ReactDOM.createRoot(rootNode).render(<App />);
JavaScript
복사

리액트 Hook 사용 규칙

useState()를 비롯한 모든 리액트 Hook함수형 컴포넌트의 최상위에서만 선언되어야 합니다.
import { useState } from 'react'; const App = () => { // 🙆🏻‍♂️ 함수형 컴포넌트의 최상위(top level) 위치에서만 리액트 Hook을 사용할 수 있습니다. const [color, setColor] = useState('blue'); return ( <> <h1 style={{ color }}>색상: {color}</h1> <button onClick={() => setColor('red')}> Red로 변경 </button> </> ); };
JavaScript
복사
아래의 경우처럼 리액트 Hook을 사용하는 경우는 없어야합니다.
import { useState } from 'react'; const THEME = 'light'; const App = () => { // 🙅🏻‍♂️ 조건문 안에서 사용할 수 없습니다. if (THEME === 'light') { const [color, setColor] = useState('blue'); } // 🙅🏻‍♂️ 반복문 안에서 사용할 수 없습니다. if (let i = 0; i < 10; i++) { const [color, setColor] = useState('blue'); } // 🙅🏻‍♂️ 중첩된 함수에서 사용할 수 없습니다. const getColorState = ()=> { const [color, setColor] = useState('blue'); } return ( <> <h1 style={{ color }}>색상: {color}</h1> <button onClick={() => setColor('red')}> Red로 변경 </button> </> ); };
JavaScript
복사

useState로 Counter 앱 구현

간단한 리액트 카운터 앱 코드를 보면서 useState()를 이해해 봅시다. 아래 Counter 컴포넌트는 현재 카운트가 몇인지 count 상태를 사용해서 보여주고 있고, +버튼을 클릭하면 setCount() 상태 변경 함수를 통해 count 상태를 1씩 증가시킵니다. count 상태가 변경될 때마다, 컴포넌트 랜더링이 다시 수행되므로 변경된 값이 화면에 적용되어 나타나게 됩니다.
import { useState } from 'react'; import ReactDOM from 'react-dom/client'; // Counter 컴포넌트 const Counter = () => { // useState()를 사용해 count 상태 생성 const [count, setCount] = useState(0); // count 상태를 증가시키는 함수 const increase = (value) => { setCount(count + value); }; return ( <> <h1>현재 카운트: {count}</h1> {/* +버튼을 클릭했을때 count 1씩 증가 */} <button onClick={() => increase(1)}> + </button> </> ); }; // Counter 컴포넌트 랜더 const rootNode = document.getElementById('root'); ReactDOM.createRoot(rootNode).render(<Counter />);
JavaScript
복사
위 코드의 increase() 함수를 보면 count 상태를 변경할때 현재 count 상태 값에 인자로 받은 value 값을 더해서 setCount()의 인자로 직접 포함시키는 식으로 처리하고 있습니다. value가 1이라면 setCount(count + 1)처럼 직접 인자로 연산해서 포함한 다음 상태 변경하는 식이죠. 지금처럼 간단한 처리를 하는 경우엔 별다른 문제가 되진 않지만, increase() 함수가 보다 복잡한 처리를 하게 되는 경우 이렇게 직접 처리하게 되면 문제가 발생할 수 있습니다.
예를 들어 아래 코드의 increaseTwice() 함수처럼 setCount()를 기존과 같은 식으로 두번 실행하는 경우, 버튼을 클릭해서 increaseTwice()가 실행되더라도 setCount()는 한번만 처리된 것과 같은 결과가 나오게 됩니다.
import { useState } from 'react'; import ReactDOM from 'react-dom/client'; const Counter = () => { const [count, setCount] = useState(0); const increaseTwice = (value) => { // 같은 setCount 함수를 두번 실행 setCount(count + value); setCount(count + value); }; return ( <> <h1>현재 카운트: {count}</h1> <button onClick={() => increaseTwice(1)}> 두번 증가 </button> </> ); }; const rootNode = document.getElementById('root'); ReactDOM.createRoot(rootNode).render(<Counter />);
JavaScript
복사
 클릭할 때마다 setCount(count + 1)를 두번 실행했으니 2씩 증가해야하지만 결과는 1씩만 증가하는걸 볼 수 있습니다.
위 코드처럼 기존 상태를 연산해서 새로운 상태로 변경하는 경우, 상태 변경을 함수식으로 처리해야 합니다. 함수식으로 변경하게 되면 변경하기 전의 현재 상태 값을 인자로 받게 되는데 이 현재 상태 값을 사용해서 새로운 상태 값을 연산한 뒤 리턴해주면 됩니다.
setState((currentState) => { // 현재 상태를 인자로 받고, 새로운 상태를 연산해서 리턴하면 새로운 상태로 변경됩니다. const newState = currentCount + someValue; return newState; });
JavaScript
복사
카운터 앱의 상태변경을 함수식으로 처리하도록 수정해보면 의도한대로 값이 두번씩 증가하는 것을 볼 수 있습니다.
import { useState } from 'react'; import ReactDOM from 'react-dom/client'; const Counter = () => { const [count, setCount] = useState(0); const increaseTwice = (value) => { // setCount를 함수식으로 처리하도록 수정 setCount((currentCount) => { return currentCount + value; }); setCount((currentCount) => { return currentCount + value; }); }; return ( <> <h1>현재 카운트: {count}</h1> <button onClick={() => increaseTwice(1)}> 두번 증가 </button> </> ); }; const rootNode = document.getElementById('root'); ReactDOM.createRoot(rootNode).render(<Counter />);
JavaScript
복사

 useEffect

React Hooks 목록