본문 바로가기
Study/react.js

[React Hook] 리액트 훅의 state 관리 함수 useState() 함수를 사용해보자.

by 박히밍 2022. 9. 25.
반응형

React

[React Hook] 리액트 훅의 state 관리 함수 useState() 함수를 사용해보자.

오늘은 리액트 훅 중 하나인 useState() 함수 사용법을 알아보려 한다.

오늘 글에서는 깊은 동작원리에 대해서는 다루지 않을 예정이다. 

(나는 쪼렙 코린이니까,, 기회가 된다면 동작원리는 나중에 공부해서 정리할 거임.)

useState() 함수를 사용하기 위한 사용 문법과 간단한 예제를 통해 가볍게 이해할 수 있도록 준비했다.

 

 

 

 

들어가기 전

 

State란?

리액트 컴포넌트가 가질 수 있는 동적인 상태값을 일컫는 말이다.

 

React Hook은 뭔데?

리액트 훅은 리액트 버전 16.8부터 새로 추가된 요소로, 리액트 훅이 추가되기 전에는 class 기반 컴포넌트로 상태 값을 관리했었으나, class 기반 컴포넌트는 간단한 상태값 조차 관리하기 쉽지 않았다. 하지만 리액트 훅의 useState() 함수가 추가되면서 함수형 컴포넌트에서도 상태값을 쉽게 관리할 수 있게 된 것이다.

 

 

 


 

 

 

1.  useState() 함수 사용법

import

import { useState } from 'react';

 

state 선언

const [state, setState] = useState(초기값);

/* 숫자형 */
const [state, setState] = useState(0);

/* 문자형 */
const [state, setState] = useState('');

/* Array */
const [state, setState] = useState([]);

/* object */
const [state, setState] = useState({});

/* boolean */
const [state, setState] = useState(true);

위와 같이 작성해주면 된다.

import 선언은 최상단에 작성하고 state선언은 컴포넌트 안에 넣으면 된다.

state 값을 변경할 함수의 이름은 보통 set스테이트 변수명  이렇게 작명하여 대부분 사용하고 있다.

 

 

state를 선언하는 방법은 자바스크립트의 구조 분해 할당 문법(Destructuring assignment)으로 이전 게시글을 참고하면 이해하기 쉽다.

https://heeeming.tistory.com/6?category=928305

 

[JS ES6] 자바스크립트 구조 분해 할당(Destructuring assignment)

JS 구조 분해 할당(Destructuring assignment)이란? 개발할 때 키를 가진 데이터 여러 개를 하나에 저장할 땐 객체를, 데이터를 순서대로 저장할 땐 배열을 사용한다. 이 배열과 객체의 전체 또는 일부를

heeeming.tistory.com

 

 

 

전체적 사용법을 보자면 아래와 같다.

import { useState } from 'react';

function App() {
  const [number, setNumber] = useState(0);
  return (
    <div className="App">
      <h1>숫자 카운트: {number}</h1>
      <button onClick={()=>{ setNumber(number+1)}}>button</button>
    </div>
  );
}

export default App;

 

h1 태그 내 콧수염 괄호 안에 number state를 바인딩해주고

버튼을 클릭할 때마다 setNumber() 함수를 실행하도록 작성한다.

버튼을 클릭할 때마다 setNumber() 함수 내에선 number의 값을 1씩 증가하도록 기능이 구현되어있다.

 

숫자데이터 값이 1씩 증가하여 렌더링 되고 있다.

 

위와 같이, 리액트는 state 값이 변경될 경우 state 값이 포함된 html을 재렌더링 해준다.

그렇기 때문에 자주 변경될 것 같은 값들을 state에 담아 바인딩하면 알아서 변경된 값을 감지하여 html에 뿌려주는 것이다.

예) 글 제목, 상품 금액, 날짜, 좋아요 수 등

 

 

 


 

 

state에 Array를 담아 그 값을 변경해보자.

import { useState } from 'react';
import './App.css';

function App() {
  const [movie, setMovie] = useState(['어바웃타임','관상','아이언맨']);
  return (
    <div className="App">
      <h1>영화리스트0: {movie[0]}</h1>
      <h1>영화리스트1: {movie[1]}</h1>
      <h1>영화리스트2: {movie[2]}</h1>
      <div>
        <button onClick={()=> {
          let copy = movie;
          copy[0] = '겨울왕국';
          setMovie(copy)
        }}>
          리스트 변경
        </button>
      </div>
    </div>
  );
}

export default App;

    

영화리스트가 렌더링 된 화면

 

movie state에 영화 제목들을 문자형 배열로 담고 state 변경 함수를 setMovie로 지정해주었다.

각 movie[0] 부터 movie[2] 까지 화면에 뿌려지도록 바인딩하였고.

리스트변경이라는 버튼을 클릭하면 movie state의 0번째 배열(어바웃타임)을 겨울왕국으로 변경하도록 setMovie안에 작성하였다.

movie state를 copy 변수에 담은 이유는 객체나 배열의 경우 원본 데이터를 직접 조작하도록 하는 것보다 기존 데이터는 보존하도록 코드를 작성하는 것이 좋기 때문에 let copy 변수에 담아 복사하였다.

리스트 변경 버튼을 클릭 후 실행 결과를 미리 예측해보자면, movie state는 ['겨울왕국', '관상', '아이언맨']이 될 것이다.

한 번 실행해보자.

 

엥 안되는디요?

 

위와 같이 어바웃타임이 > 겨울왕국으로 변하지 않는 것을 확인할 수 있을 것이다.

왜 그럴까?

setState는 state의 상태값이 변화하면 그 값으로 새롭게 렌더링 되도록 도와주는 기능을 하는데,

state의 값이 변화되었다고 인식하지 못했기 때문이다.

 

엥, 먼솔 copy[0] = '겨울왕국'으로 변경해줬잖아?


import { useState } from 'react';
import './App.css';

function App() {
  const [movie, setMovie] = useState(['어바웃타임','관상','아이언맨']);
  return (
    <div className="App">
      <h1>영화리스트0: {movie[0]}</h1>
      <h1>영화리스트1: {movie[1]}</h1>
      <h1>영화리스트2: {movie[2]}</h1>
      <div>
        <button onClick={()=> {
          let copy = movie;
          copy[0] = '겨울왕국';
          /* setMovie(copy) */
          if(movie === copy ) {
            console.log('true');
          } else {
            console.log('false');
          }
        }}>
          리스트 변경
        </button>
      </div>
    </div>
  );
}

export default App;

 

위와 같이  코드를 변경해보자.

if 조건문에 movie와 copy가 같은 값이면 콘솔창에 true를 출력할 것이고, 아니라면 false를 출력할 것이다.

 

 

 

대혼돈의 카오스,,,

 

콘솔창에는 true가 출력되었다.

왜일까? movie 배열의 값과 copy 배열의 값은 서로 다르기 때문에 false가 나오리라 생각했지만 true를 뱉어내었다.

 

그 이유는 자바스크립트가 Array나 Object, function의 경우 자료를 하나 만들면 Ram이라는 가상공간에 자료를 저장하게 된다.

그리고 변수를 생성할 경우 그 값을 할당하는 것이 아니라, 변수는 그 값이 Ram의 어디에 저장되어있는지 가리키는 화살표 역할을 하게 되기 때문에,

 

 

let copy = movie;
copy[0] = '겨울왕국';

Array나 Object를 이렇게 복사해도 값을 새로운 Ram에 저장하는 게 아니라 결국엔 같은 화살표를 가리키고 있기 때문에 

copy[0] = '겨울왕국'으로 변경해주어도 Ram안에 있는 데이터 '값'만 변했을 뿐, 화살표 자체가 변하는 것은 아니라서 true를 반환하는 것이다. 이를 참조값(reference data type)이라고 한다.

 

 

그렇다면 어떻게 해야 setMovie() 함수가 state가 변경되었다고 인식하게 만들 수 있을까?

가장 쉬운 방법은, javascript의 참조값 얕은 복사(Shallow Copy) 방법 중 하나인 전개 구문 문법(Spread operator)을 사용하면 된다.

 

let copy = [...movie];

 

movie state에 ... 을 붙인 것이 전개 구문 문법(Spread operator)이다. Spread의 뜻을 직역하자면 흩뿌리다, 퍼트리다 란 뜻인데

전개 구문 문법을 사용하면 movie를 감싸고 있던 배열의 괄호가 사라지기 때문에 ...movie를 [] 배열 괄호로 다시 묶어주고

copy 변수에 할당하면 얕은 복사가 완료된다.

아까 조건문을 다시 돌려보자.

 

movie === copy는 같지 않기 때문에 false를 출력했다.

콘솔창에 false가 출력된 것을 확인할 수 있다.

이제 조건문을 지우고 다시 setMovie() 함수를 동작시켜보자.

import { useState } from 'react';
import './App.css';

function App() {
  const [movie, setMovie] = useState(['어바웃타임','관상','아이언맨']);
  return (
    <div className="App">
      <h1>영화리스트0: {movie[0]}</h1>
      <h1>영화리스트1: {movie[1]}</h1>
      <h1>영화리스트2: {movie[2]}</h1>
      <div>
        <button onClick={()=> {
          let copy = [...movie]; //spread operator
          copy[0] = '겨울왕국';
          setMovie(copy)
        }}>
          리스트 변경
        </button>
      </div>
    </div>
  );
}

export default App;

드디어 정상적으로 렌더링이 된다.

 

위와 같이 변경된 state를 setMovie가 감지하여 재렌더링 해준 것을 확인할 수 있다.

이렇듯 Array나 Object를 state를 수정하고 싶다면 독립적으로 카피본을 만들어 주어서 

그 값이 변화되는 것을 감지할 수 있도록 하여 주의하며 사용하도록 하자.

쉬운 게 하나도 없다..

반응형