초보 개발자의 성장 일기

[React] 새로고침없이 상태 변경하는 방법 본문

Development/React JS

[React] 새로고침없이 상태 변경하는 방법

YUNA 2024. 3. 8. 04:29

todo리스트 구현 중 O버튼을 누르면 객체 속성 중에서 done이 false에서 true로 바뀌고

style에 props로 done의 상태를 전달받아 true로 바뀌면 style이 바뀌도록 구현했다.

데이터는 localstorage로 저장했고, 배열의 상태가 변화할때마다 localstorage의 상태로 업데이트 되도록 useState를 해 놓은 상태이다.

 

삭제, 등록은 바로 리렌더링이 돼서 화면에 구현이 되는데 할일을 완료했을 때는 새로고침해야만 스타일이 반영이 됐다.

state가 변경이 되면 리렌더링이 되는데, state가 바뀌지 않고 있다고 판단이 되었고,

버튼을 클릭할 때 객체안의 done의 속성이 false에서 true로 바뀌는 순간에

"state가 변경되었다고 인식하지 못하는건가?" 라는 생각이 들었다.

 

 

📍 처음에 시도했던 방법: todoArray 속성 직접 변경

  function toggleClickHandler(e: React.MouseEvent<HTMLDivElement>) {
    e.preventDefault();
    const target = e.currentTarget as HTMLDivElement;
    const parentElement = target.parentElement;

    if (!parentElement) return;
    let currenTodoId = target.parentElement.id;
    const todoIndex = todoArray.findIndex((item) => item.id === currenTodoId);

    if (todoIndex !== -1) {
      const currentState = !todoArray[todoIndex].done;
      todoArray[todoIndex].done = currentState;
      localStorage.setItem(TODO_KEY, JSON.stringify(todoArray));
    }
  }

처음에 내가 구현했던 방법에 문제가 있음을 알았다.

클릭한 이벤트의 부모를 찾고,

아이디가 투두리스트 배열에 몇번째 인덱스인지 찾은 후 인덱스 번호가 존재하면 현재 상태의 반대를 상수로 저장해서

배열의 인덱스 번호의 상태를 저장한 변수로 다시 넣고 다시 localstorage넣도록 구현했는데

setTodoArray를 이용해서 상태변경을 하지 않고 todoArray에 직접 변경한 것이 문제였던것 같다.

 

 

📍 변경한 방법: setTodoArray에 배열을 복사해 속성 변경 후 리턴

  function toggleClickHandler(e: React.MouseEvent<HTMLDivElement>) {
    e.preventDefault();
    const target = e.currentTarget as HTMLDivElement;
    const parentElement = target.parentElement;

    if (!parentElement) return;
    let currenTodoId = target.parentElement.id;
    const todoIndex = todoArray.findIndex((item) => item.id === currenTodoId);

    if (todoIndex !== -1) {
      const currentState = !todoArray[todoIndex].done;

      setTodoArray((prevTodoArray) => {
        const updatedTodoArray = [...prevTodoArray];
        updatedTodoArray[todoIndex] = {
          ...updatedTodoArray[todoIndex],
          done: currentState,
        };
        return updatedTodoArray;
      });

      localStorage.setItem(TODO_KEY, JSON.stringify(todoArray));
    }
  }

setTodoArray로 다시 상태 변경을 해 주었다.

이전의 배열을 가져와서 새로운 상수에 복사하고 복사한 상수의 인덱스의 상태를 변경해주고 업데이트된 상수 배열을 리턴해준다.

이렇게 하면 새로고침 하지 않아도 state가 변경되고 리렌더링이 되서 스타일이 바로 적용된다.

 

 

 

React는 상태가 변경되면 얕은 비교(참조값 비교)를 해서 이전 값과 현재 값이 같은 객체인지 확인한다.

리렌더링이 발생하지 않은 이유는 todoArray객체의 done의 상태만 업데이트 했고, todo가 가리키는 참조값이 업데이트 된 것이 아니기 때문이다. setTodoArray가 호출되기 전과 후의 todo를 같은 주소로 갖는 동일한 객체로 판단하기 때문에 어떠한 변화도 감지하지 못한 것이다.

리액트는 불변성이 유지되어야 하기 때문에 상태의 자료형이 참조형이면 완전히 새로운 참조값을 갖는 상태로 업데이트 해야지 이전의 상태를 직접 건드려서 일부만 변경해서는 안된다.