본문 바로가기
TIL/React

리액트 상태변화와 랜더링 그리고 자바스크립트의 참조형 데이터

by Dev_Dank 2021. 7. 3.

오늘은 Academind 에서 리액트 강의를 듣고 실습을 진행 하고 있었다. 그런데 큰 문제가 하나 있었는데....

아니 분명 useState hooks로 상태를 변화 시켰는데 컴포넌트가 새롭게 렌더링이 되지 않는 것이다.

import React, { useState } from "react";

import "./App.css";
import InputCard from "./components/InputCard/InputCard";
import AddedList from "./components/AddedList/AddedList";

let userData = [];

function App() {
  const [submittedData, setSubmittedData] = useState(userData);

  const handleData = (data) => {
    userData.push(data);
    setSubmittedData(userData);  //여기서 아무리 상태를 변화해도 AddedList가 리랜더링이 안된다....
    console.log("app", submittedData); //콘솔에서는 분명 상태가 변화된걸로 찍히는데...
  };

  return (
    <div>
      <InputCard onUserSubmit={handleData} />
      <AddedList data={submittedData} />
    </div>
  );
}

export default App;

분명 강의에서는 상태가 변하면 리액트는 상태변화를 감지해서 컴포넌트를 리랜더링 한다고 알려줘서 당연히 상태만 변화시켜면 리랜더링이 될거라고 생각했는데 "왜 그럴까......."생각하며 한참을 삽질을 했다.(하루죙일 걸렸다 ㅜㅜ)

맨처음에는 강의에서 뭔가 빠트리고 안알려준게 있나 해서 공식문서를 가서 찾아봤다. 

일단 훅 API 설명에서는 내 증상과 관련된 말이 따로 없었다... 

그래서 생각해보니 리액트 useState훅도 사실상 setState()와 마찬가지니 setState() 설명을 찾아봤는데 아니나 다를까 관련 내용이 있었다. 

https://reactjs.org/docs/react-component.html#setstate
https://reactjs.org/docs/react-component.html#setstate

상태변화가 리렌더링을 완전히 보장하는 것은 아니라는것 그리고 상태변화 이후 리랜더링을 하려면 콜백함수 형태로 넘겨주어야 리랜더링이 보장이 된다한다. 그런데 나는 setSubmittedData에 그냥 외부 배열을 전달만 해주고 있었을 뿐이다. 

그렇다면 콜백함수 형태로 변화 시켜서 사용하면 어떨까 싶어서 setSubmittedData 안의 인자를 콜백함수 형태로 변화시켜사용 해봤다.

  const handleData = (data) => {
    userData.push(data);
    setSubmittedData(() => {return userData});
    console.log("app", submittedData);
  };

아... 이래도 안된다 ㅜㅜ

한참을 더 삽질하다 스택오버플로우에서 결국 답을 찾았다. 

https://stackoverflow.com/questions/56266575/why-is-usestate-not-triggering-re-render

 

Why is useState not triggering re-render?

I've initialized a state that is an array, and when I update it my component does not re-render. Here is a minimal proof-of-concept: function App() { const [numbers, setNumbers] = React.useState(...

stackoverflow.com

문제는 내가 상태를 업데이트 할때 똑같은 배열(userData)을 그대로 이용하고있다는 것이었다. 

????? 나는 분명 push로 userData배열에 데이터를 추가했는데 왜 데이터가 추가된 배열이 똑같은 배열이라는 것일까?

정답은 배열데이터는 자바스크립트에서 참조형이기 때문이다. 즉 userData라는 식별자가 가리키는 것은 해당 배열의 시작 메모리 주소일 뿐이지 실제로 배열내부의 모든 데이터를 나타내는 것이 아니다. 

아무리 배열내에 데이터를 추가(push)한다해도 결국 userData 변수가 가리키는 대상이 변하지 않기 때문에 아무리 리액트입장에서는 상태변화가 없다고 생각한다는 것이다. 

그래서 아래와 같이 코드를 고쳤더니 이제야 제대로 돌아갔다....

  const handleData = (data) => {
    setSubmittedData((prevData) => {
      return [data, ...prevData];
    });
  };

 

'TIL > React' 카테고리의 다른 글

Redux를 쓰는이유  (0) 2021.07.11
리액트의 작동 방식  (0) 2021.07.06

댓글