[Hook] - useState

Donggu(oo)·2022년 12월 7일
0

React

목록 보기
15/30
post-thumbnail

1. useState


  • state를 사용하기 위한 훅으로, 함수형 컴포넌트에서는 기본적으로 state라는 것을 제공하지 않기 때문에 클래스 컴포넌트처럼 state를 사용하려면 useState() 훅을 사용해야 한다.

  • state 값이 변경될 때 마다 리렌더링 된다.

  • state를 직접 수정하면 리액트는 render 함수를 호출하지 않아서 렌더링이 일어나지 않는다. 그래서 setState를 호출하여 state를 변경해야 리액트 엔진이 render 함수를 이용해서 렌더링을 실행한다.

Hook이란?

  • Hook은 React 16.8에 새로 추가된 기능으로 Hook은 class를 작성하지 않고도 state와 다른 React의 기능들을 사용할 수 있게 해준다.
  • Hook은 함수 컴포넌트에서 React state와 생명주기 기능(lifecycle features)을 “연동(hook into)“할 수 있게 해주는 함수이다.
  • Hook은 최상위에서만 호출해야 한다. 반복문, 조건문, 중첨된 함수 내부에서 실행할 수 없다.
  • Hook은 React 함수 컴포넌트 내부에서만 호출해야 한다.

1) import

import { useState } from 'react'

2) 선언

  • useState를 컴포넌트 안에서 호출한다. useState를 호출한다는 것은 state라는 변수를 선언하는 것과 같으며, 이 변수의 이름은 아무 이름으로 지어도 된다.

  • 일반적인 변수는 함수가 끝날 때 사라지지만, state 변수는 React에 의해 함수가 끝나도 사라지지 않는다.

기본 구문

const [변수명, set함수명] = useState(상태 초기값)

매개 변수

  • 변수명 : 상태 값 저장 변수, 상태 초기값을 지정해주면 state에 그 값이 담긴다.

  • set함수명 : 상태 값 갱신 변수, 상태 변경 함수에 어떤 값을 부여하던 그 값으로 업데이트하고 리렌더링 한다.

  • useState를 콘솔창에서 보면 순서대로 초기값과 state를 바꿀 때 사용하는 함수를 갖는다.

  • state 변수는 구조분해할당을 사용하여 선언한다.
function CheckboxExample() {
// 새로운 state 변수를 선언하고, 여기서는 이것을 isChecked 라고 부른다.
  const [isChecked, setIsChecked] = useState(false);
}
// 위의 구조분해할당을 풀어쓰면 아래와 같다.
const stateHookArray = useState(false);
const isChecked = stateHookArray[0];
const setIsChecked = stateHookArray[1];

3) 사용 형태

  • 아래는 버튼을 클릭할 때 마다 숫자가 1 증가하는 counter 예제다.

  • 아래와 같이 카운트를 함수의 변수로 선언에서 사용하게 되면 버튼 클릭 시 카운트 값을 증가시킬 수는 있지만, 재렌더링이 일어나지 않아 새로운 값이 화면에 표시되지 않는다.

  • 버튼을 클릭한 후 증가된 count 값을 확인하려면 콘솔창에서 확인해야 한다.

import React, { useState } from 'react';

function Counter() {
    const count = 0;

    return (
        <div className='plus'>
            <div>{count}번 클릭했습니다.</div>
            <button onClick={() => count = count + 1}></button>
        </div>
    );
}
  • 버튼이 눌렸을 때 setCount() 함수를 호출하여 카운트를 1 증가시킨다. 그리고 count의 값이 변경되면 컴포넌트가 재렌더링되면서 화면에 새로운 카운트 값이 표시된다.
import React, { useState } from 'react';

function Counter() {
    const [count, setCount] = useState(0);

    const increase = () => {
        setCount(count + 1)  // setCount 함수를 통해 count 값을 1씩 증가시킴
    }

    return (
        <div className='plus'>
            <div>{count}번 클릭했습니다.</div>  // 현재 카운트 숫자 표시(초기값을 0으로 설정함)
            <button onClick={increase}>plus</button>
        </div>
    );
}

2. 이전 상태에 의존하는 상태 업데이트하기


  • 아래의 코드에서 두 개의 titleChangeHandler는 대체로 모두 통할 것이다.

  • 리액트는 상태 업데이트를 즉시 처리하지 않고 예약한다. 따라서 첫 번째 방법을 사용하여 다수의 업데이트를 동시에 예약할 경우 오래되었거나 잘못된 상태에 의존하게 될 수도 있다.

  • 하지만 두 번째 방법의 경우 리액트가 제공하는 내부 함수에서 예약된 모든 상태 업데이트를 기억하고선 상태가 항상 최신 상태가 되도록 보장해 준다. 따라서 두 버째 방법이 항상 최신 상태로 작업할 수 있는 더 안전한 방법이다.

const [userInput, setUserInput] = useState({
  enteredTitle: '',
  enteredDate: '',
});

const titleChangeHandler = (e) => {
  setUserInput({
    ...userInput,
    enteredTitle: e.target.value,
  });
};

// 이전의 상태에 의존해서 상태를 업데이트하는 올바른 방법
const titleChangeHandler = (e) => {
  setUserInput((prevState) => {
    return { ...prevState, enteredTitle: e.target.value };
  });
};
  • setCount에서 현재 state를 바탕으로 다음 state를 계산하고 싶다면, setCount((currnetValue) => currentValue + 1)와 같이 함수를 사용해야 한다.
import React, { useState } from 'react';

function Counter() {
    const [count, setCount] = useState(0);

    const increase = () => {
      setCount((prevState) => prevState + 1)
    }

    return (
        <div className='plus'>
            <div>{count}번 클릭했습니다.</div>  // 현재 카운트 숫자 표시(초기값을 0으로 설정함)
            <button onClick={increase}>plus</button>
        </div>
    );
}

참고

3. useState lazy initialization


  • useState lazy initialization(게으른 초기화)는 useState 초기값에 직접적인 값 대신에 함수를 넘기는 것을 말한다.

  • useState의 인자에는 number, string, array, object 어떤 것이든 상태 초기값으로 넣어 할당 할 수 있지만, 함수도 초기값으로 할당 할 수 있다.

  • useState lazy initialization는 초기값이 복잡한 연산을 포함하고 있을 때 사용하며, useState lazy initialization함수는 오직 state가 처음 만들어 질 때만 실행되고, 이후 다시 리렌더링이 된다면 이 함수의 실행은 무시된다.

  • 초기값이 복잡한 연산의 예
  1. localStorage의 접근
  2. filert, map, find 등의 배열 조작
  3. new Date()
  4. 그 외 일반적인 함수를 통해서 값을 구해야 하는 경우
  • 다시 말해, useState는 그 함수가 처음 렌더링 될 때 작동하며, 이는 todoData state의 초기값을 만든다. setTodoData가 실행되면, 전체 함수가 다시 실행되며, todoData의 값을 업데이트 한다. 이는 todoData의 값이 변경될 때 마다 리렌더링을 발생시키며, 다시 말해 이 초기값은 다시 쓰일 일이 없게 된다.
  • localStorage의 값은 최초 렌더링 시 한 번만 불러오면 된다. 그러나, setState의 초기값에 localStorage의 불러오는 코드를 직접적인 값으로 넣게 된다면 리렌더링 될 때마다 불러오므로 이는 필요없는 계산을 계속해서 하게 되는 것이다.
  • 그러나 화살표 함수로 넣게 된다면 처음 렌더링 될 때 한번만 localStorage의 값을 불러오기 때문에 불필요한 계산을 막을 수 있다.
// 초기값에 직접적인 값을 바로 넣은 상태
const [todoData, setTodoData] = 
      useState(JSON.parse(window.localStorage.getItem('localData')) || dummyData);

// 초기값에 즉시 실행 화살표 함수로 넣은 상태
const [todoData, setTodoData] = 
      useState(() => JSON.parse(window.localStorage.getItem('localData')) || dummyData);

참고한 글
useState lazy initialization

0개의 댓글