프론트엔드 개발/React

React 기초) useState 배열 상태 값 변경하는 방법(spread 문법)

Ella Seon 2022. 11. 22. 11:32

리액트 useState 배열 변경하는 법

 

 

0. 글을 쓰게된 이유

- useState를 이용하여 버튼을 클릭하면, 배열의 값을 변경하려고 하는데... 아무리 해도..안바껴서..

- spread 문법이 필요하다는 것을 알게됨

 

1. 예시 코드

- useState에 기념일들이 배열로 나열되어있고, html 태그에 각 기념일들이 데이터 바인딩 되어있다. 

useState에 대해서 알고싶다면 아래의 글 참고 부탁드립니다.

2022.11.21 - [프론트엔드 개발/리액트] - React 기초) state값이 자꾸 바뀔때는? useState

- 9월 22일 기념일을 수정 버튼을 누르면 10월22일로 바뀌었으면 좋겠다. 어떻게 하면 좋을까??

 

App.js 파일

import { useState } from "react";

function App() {
  let [날짜, 날짜변경함수] = useState(["9월22일", "11월23일", "12월23일"]);

  return (
    <div>
      <h1>기념일리스트</h1>
      <p>{날짜[0]}</p>
      <p>{날짜[1]}</p>
      <p>{날짜[2]}</p>
      <button>수정</button>
    </div>
  );
}
export default App;

 

 

2.  먼저 하드코딩으로 논리의 흐름을 잡아보자

- 먼저, useState의 기본 특징을 이용해서 9월 22일 기념일을 10월22일로 변경해보자.

날짜 변수에 useState로 초기값을 지정해주고, 날짜변경함수() 인자 안에 들어있는 값으로 값을 변경할수있다는 것을 알고있을 것이다. 

아래 예시처럼 날짜 변수의 변경될 값을 setDate 함수 안에 일일히 하나하나씩 입력해서 값을 변경해보자

 

이제 수정 버튼을 누르면 useState 안에 있는 초기값이 날짜변경함수 안 인자로 변경되어 9월 22일이 10월22일로 바뀌는걸 볼수있다. 

 

그런데 이러한 방법은 확장성이 낮다. useState 안 데이터가 만약 100개라면? 1000개라면 일일히 다 바꿀것인가?

import { useState } from "react";

function App() {
  let [날짜, 날짜변경함수] = useState(["9월22일", "11월23일", "12월23일"]);


  function setDate(){
    날짜변경함수(["10월22일", "11월23일", "12월23일"])
  }

  return (
    <div>
      <h1>기념일리스트</h1>
      <p>{날짜[0]}</p>
      <p>{날짜[1]}</p>
      <p>{날짜[2]}</p>
      <button onClick={setDate}>수정</button>
    </div>
  );
}
export default App;

 

 

3. 조금더 확장성 있는 코드로 건들여보자

- 배열의 원본 값을 수정할때는 

변수명[인덱스] = 변경된값

을 넣어서 원본으로 수정하면 된다. 

따라서 나는 9월 22일에서 10월 22일로 바꾸고 싶으니 날짜[0] = '10월 22일' 로 변경해주었다.

- 원본 값을 수정한 후 날짜변경함수() 소괄호 안 인자안에 변경하고 싶은 값을 넣으면 된다.

 

그런데 여기서 왜 [...날짜] 라고 접근했을까? ... 문법은 spread 문법이다. 

왜 spread 문법을 사용했을까?

그냥, 날짜변경함수(날짜) 라고 하면 변경되지가 않는다. 왜??

/* eslint-disable */

import { useState } from "react";

function App() {
  let [날짜, 날짜변경함수] = useState(["9월22일", "11월23일", "12월23일"]);

  function setDate(){
    날짜[0] = '10월22일';  //바꾸고 싶은 배열 값 수정
    날짜변경함수([...날짜]); //(날짜) 가 아니라 ([...날짜])로 spread를 사용해서 변경해야함
  }

  return (
    <div>
      <h1>기념일리스트</h1>
      <p>{날짜[0]}</p>
      <p>{날짜[1]}</p>
      <p>{날짜[2]}</p>
      <button onClick={setDate}>수정</button>
    </div>
  );
}
export default App;

 

 

3-1) setState 즉, 상태를 변경해주는 함수의 동작원리

1. state 변경함수를 쓸때( 위 예제에서는 날짜변경함수가 될것임)

기존의 state 와 신규 state를 비교를 해본다.(기존 state === 신규 state)

만약, 기존 state와 신규 state 값이 같을시 바뀐값으로 변경이 되지 않는다. 

즉, true가 나오면 값이 안바뀌는것이다.

 

2. 만약  spread를 쓰지 않고, 아래와 같이 해놓으면

 

  function setDate(){
    날짜[0] = '10월22일';
    날짜변경함수(날짜);
  }

날짜[0]에서 변경된 값을 재할당 하였기에 기존 state가 이미 10월22일로 원본이 바뀌어버렸기에  기존 state와 신규 state값이 같아져버린다. (원본이 바뀌는 이유에 대해서는 reference data 에 대해 조사하면 된다)

 

그렇기에  날짜변경함수(날짜) 의 기본값이 10월22일로 변하지 못하고, 그대로 9월22일이라는 값이 나와버린다.

true가 나왔기때문에 값이 바뀌어지지가 않는다.

 

기존 state 변경 state (신규 state)
useState(["10월22일", "11월23일", "12월23일"]) useState(["10월22일", "11월23일", "12월23일"])

그러면 기존 state와 변경 sate중 하나를 다르게 해서 false 가 나와야한다. 어떻게 false가 나오게 할까?

spread를 사용한다.

 

 

3-2) spread 문법

spread는 아래와 같이 배열, 문자열,객체의 괄호를 벗겨버리는 역할을 하는데.

//...을 쓰면 대괄호 쉼표를 다 벗겨버린다. 

let 배열 = [1,2,3]
console.log(...배열) // 1 2 3

let 문자열 = '안녕하세요'
console.log(...문자열) // 안 녕 하 세 요

주로 값을 복사하는데 사용한다.

//주로 값을 복사할때 쓴다. 왜냐하면, 값 공유를 하지 않기때문에 원본을 수정하지 않고 값 복사를 할 수 있다.

let data = [1,2,3]
let 복사data = data 
data[0] = 2

console.log(복사data) //[2, 2, 3] 나는 분명히 data의 값만 바꿨을 뿐인데 복사data까지 값이 바껴버렸다.


//그냥 나는 태초의 값만 복사하고... 복사한 값은 변경 안되게 하고싶었는데 ?? 어떻게 해야하지?
//그럴때 쓰는게 spread 문법

let data = [1,2,3]
let 복사data = [...data]

data[0] = 2
console.log(복사data //[1, 2, 3]  spread를 쓰면 내가 수정하고 싶은 값만 수정할 수 있다.

 

useState 코드 예시를 다시보자

spread 를 이용해서 날짜의 값의 괄호를 제거해준다음에 다시 괄호를 씌우는 거다. 

이렇게 하면 a와 b 변수의 값 공유가 일어나지 않는다.

 

  function setDate(){
    날짜[0] = '10월22일';
    날짜변경함수([...날짜]);
  }

 

이러한 spread 를 사용한 행위를 불변성을 지킨다 라고 부른다.

불변성을 지켜주어야만 리액트 컴포넌트에서 상태가 업데이트 됐음을 감지하고 이에 따라 필요한 리렌더링이 진행된다. 

만약에 날짜변경함수(날짜) 로 spread를 쓰지 않고 기존 상태를 직접 수정하게 되면 값을 바꿔도 리렌더링이 되지 않는다.

 

고급진 말로 다시 풀어쓰자면, 

리액트 컴포넌트가 리렌더링을 할 때, virtual DOM 에서 이전 컴포넌트와 새로 바뀌는 컴포넌트를 비교하게 된다. 기존 배열을 직접 수정하게 되면 새로 바뀐 컴포넌트에서는 이전 컴포넌트와 같은 상태를 가질 수 있으니 ... 을 통해 새로운 배열을 만들어 변화를 주어야한다.

 

 

4. 그런데 배열은 원본 데이터를 변경하지 않는게 좋다(나중에 원본이 필요할수도 있으니)

- copy 변수를 새로 만들어서 거기에 값을 공유하지 않는 [...날짜] 복사본을 넣고

copy[0] 을 10월22일로 변경하고 copy로 state를 바꾸는 것이다.

/* eslint-disable */

import { useState } from "react";

function App() {
  let [날짜, 날짜변경함수] = useState(["9월22일", "11월23일", "12월23일"]);

  function setDate(){
    let copy = [...날짜]
    copy[0] = '10월22일';
    날짜변경함수(copy);
  }

  return (
    <div>
      <h1>기념일리스트</h1>
      <p>{날짜[0]}</p>
      <p>{날짜[1]}</p>
      <p>{날짜[2]}</p>
      <button onClick={setDate}>수정</button>
    </div>
  );
}
export default App;

 

5. 결론 : 배열이나 객체를 업데이트 할때는 기존 배열/객체를 직접 수정하면 안되고, 새로운 객체를 만들어서 새 객체에 변화를 주어야한다. 

 

 

 

 

출처: 코딩애플 array, object state 변경하는 법 수업 flow를 참고하여 작성함(예제는 전부 다름)

https://react.vlpt.us/basic/09-multiple-inputs.html

 

9. 여러개의 input 상태 관리하기 · GitBook

9. 여러개의 input 상태 관리하기 지난 튜토리얼에서 우리는 input 상태를 관리하는 방법에 대하여 알아보았는데요, 이번에는 input 이 여러개일때는 어떻게 관리해야 하는지 알아보겠습니다. 우선

react.vlpt.us