React - 불필요한 Effect 제거하기 part.2

sarang_daddy·2023년 8월 25일
0

React

목록 보기
14/26
post-thumbnail

Effect의 의존성 배열객체를 사용하면 생기는 문제점을 이해하고 내 코드를 수정해보자.

의존성 배열에 객체, 배열 사용하면 생기는 문제점

반응형 값으로 객체, 배열을 사용한 경우 변경된 값이 없어도 Effect가 실행될 수 있다.

import { useState, useEffect } from "react";
import { createConnection } from "./chat.js";

const serverUrl = "https://localhost:1234";

function ChatRoom({ roomId }) {
  const [message, setMessage] = useState("");

  const options = {
    serverUrl: serverUrl,
    roomId: roomId,
  };

  useEffect(() => {
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, [options]);

  return (
    <>
      <h1>Welcome to the {roomId} room!</h1>
      <input value={message} onChange={(e) => setMessage(e.target.value)} />
    </>
  );
}

export default function App() {
  const [roomId, setRoomId] = useState("general");
  return (
    <>
      <label>
        Choose the chat room:{" "}
        <select value={roomId} onChange={(e) => setRoomId(e.target.value)}>
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
      </label>
      <hr />
      <ChatRoom roomId={roomId} />
    </>
  );
}
  • 위 예제에서 roomId가 변경되면 options가 변경되고 Effect가 일어난다 (연결).
  • 문제는 의존성 배열로 options 객체가 사용되었다.
  • 이 예제는 setMessage로 메시지만 변경되어도 Effect가 실행된다.
  • 사용자의 의도와는 다른 불필요한 Effect가 발생하게 된다.

문제원인

자바스크립트의 객체는 평가될 때마다 새로운 객체를 생성한다.

즉, 두 객체가 동일한 값을 가지로 있더라도 다른 메모리제 저장된 별개의 객체다.

var person1 = {
  name: 'Lee'
};

var person2 = {
  name: 'Lee'
};

console.log(person1 === person2); // false
  • 때문에 roomId가 변경되지 않아도 자바스크립트는 새로운 객체(options)로 판단하고 Effect를 실행하기 때문에 생기는 문제다.

해결방법 : 객체에서 원시 값 읽기

별개의 객체라도 프로퍼티가 가리키는 원시값을 비교 평가 할 수 있다.

// 프로퍼티 값을 참조하는 person1.name과 person2.name은 값으로 평가될 수 있는 표현식이다.
// 두 표현식 모두 원시 값 'Lee'로 평가된다.
console.log(person1.name === person2.name); // true

Effect 외부의 객체에서 비교할 원시값을 읽고 동일한 을 가진 객체인지 판단할 수 있다.

function ChatRoom({ options }) {
  const [message, setMessage] = useState('');

  const { roomId, serverUrl } = options;
  useEffect(() => {
    const connection = createConnection({
      roomId: roomId,
      serverUrl: serverUrl
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId, serverUrl]); // ✅ All dependencies declared
  // ...
  • Effect 외부의 객체에서 정보를 읽어서 객체 의존성을 피한다.
  • Effect 외부의 객체에서 비교 대상값을 읽고 Effect 내부에 동일한 값을 가진 객체를 만든다.
  • 이 경우 Effect가 어떤 정보에 의존하는지 명확하게 알 수 있다.

내 코드에 적용하기

잘못된 사용

  useEffect(() => {
    const postObjectToStore = { ...postObject };
    localStorage.setItem('postObject', JSON.stringify(postObjectToStore));
    console.log('변경사항 렌더링 체크');
  }, [postObject]);
  • postObject 객체를 로컬스토리지에 저장하는데 useEffect를 사용
  • 로컬스토리지 저장에는 문제가 없지만, 의존성 배열에 postObject를 사용

  • 변경내용이 없는 postObject의 경우에도 Effect가 실행되고 있다.
  • postObject가 아닌 변경에도 Effect가 실행될 수 있다.

수정 내용

  const { title, price, content, categoryId, locationId, files } = postObject;

  useEffect(() => {
    const postObjectToStore = {
      title,
      price,
      content,
      categoryId,
      locationId,
      files,
    };
    localStorage.setItem('postObject', JSON.stringify(postObjectToStore));
    console.log('렌더링 체크');
  }, [title, price, content, categoryId, locationId, files]);
  • 의존성 배열에서 객체를 제거한다.
  • Effect 외부에서 객체의 원시값을 읽어준다.
  • Effect 의존성 배열에서 원시값을 비교한다.

  • 동일한 값을 가진 객체인 경우 Effect가 일어나지 않는다.

느낀점

자바스크립트 객체의 특성을 알고 있고 자바스크립트로 구현할때는 조심하려고 신경 썻던 부분을 리액트에서는 안일하게 생각하고 있었던 것 같다. 그래서 더욱 글로 남겨두고 싶었다. 리액트는 자바스크립트의 특징과 문법을 활용한 점을 잊지말자.

profile
한 발자국, 한 걸음 느리더라도 하루하루 발전하는 삶을 살자.

0개의 댓글