• 카테고리

    질문 & 답변
  • 세부 분야

    프론트엔드

  • 해결 여부

    해결됨

[4.4장 메모이제이션 훅] 4.4.2 useMemo 에서 every 함수에 관한 질문입니다.

24.03.25 13:07 작성 24.03.25 13:08 수정 조회수 71

1

안녕하세요 선생님 every 배열을 비교하는 방법이 궁금해서 로그를 찍어보았습니다.

// export default App;
import MyReact from "./lib/MyReact";
import React from "react";
const Board = ({ posts, tag }) => {
  MyReact.resetCursor();

  const [darkTheme, setDarkTheme] = React.useState(false);
  const filterPosts = () => {
    console.log("filterPosts");
    return posts.filter((post) => (tag ? post.tag === tag : true));
  };

  const filteredPosts = MyReact.useMemo(filterPosts, [posts, tag]);

  console.log("Board rendered");

  return (
    <>
      <div>
        <button onClick={() => setDarkTheme(!darkTheme)}>Theme Change</button>
        <span>{darkTheme ? "dark" : "light"}</span>
      </div>
      <FilteredPosts value={filteredPosts}></FilteredPosts>
    </>
  );
};

const FilteredPosts = MyReact.memo(({ value }) => {
  console.log("FilteredPosts rendered")
  return (
    <ul>
      {value.map(({ id, content, tag }) => {
        return (
          <li key={id}>
            {content} <span>#{tag}</span>
          </li>
        );
      })}
    </ul>
  );
});

export default () => {
  const [tag, setTag] = React.useState("");
  return (
    <>
      <button onClick={() => setTag("")}>ALL</button>
      <button onClick={() => setTag("tag1")}>Tag1</button>
      <button onClick={() => setTag("tag2")}>Tag2</button>
      <Board
        posts={[
          { id: "id1", content: "content1", tag: "tag1" },
          { id: "id2", content: "content2", tag: "tag1" },
          { id: "id3", content: "content3", tag: "tag2" },
        ]}
        tag={tag}
      />
    </>
  );
};
  function useMemo(nextCreate, deps) {
    console.log("deps = ", deps);
    if (!memorizedStates[cursor]) {
      const nextValue = nextCreate();
      memorizedStates[cursor] = [nextValue, deps];
      cursor = cursor + 1;
      return nextValue;
    }
    const nextDeps = deps;
    const [prevValue, prevDeps] = memorizedStates[cursor];
    console.log("prevDeps => ", prevDeps);
    console.log("nextDeps => ", nextDeps);
    if (
      prevDeps.every((prev, index) => {
        console.log(
          "comparing ->  ",
          prev,
          nextDeps[index],
          prev === nextDeps[index],
          index
        );
        return prev === nextDeps[index];
      })
    ) {
      console.log("it is same");
      cursor = cursor + 1;
      return prevValue;
    }
    console.log("it is not same");

    const nextValue = nextCreate();
    memorizedStates[cursor] = [nextValue, deps];
    cursor = cursor + 1;
    return nextValue;
  }

여기서 궁금한 점은

1번째 빨간 paragraph는 theme change를 눌러서 변경이 없는 경우입니다.

comparing => 로그를 확인하면

every가 2개를 비교하는데 첫번째는 array(posts)를 비교하고

두번째는 tag를 비교하는것을 확인 할 수 있습니다.

 

2번째 빨간 paragraph는 Tag1 버튼을 누른 경우입니다.

이때 저는 첫번째 비교는 true이고 두번째 비교에서 빈문자열 vs tag1이니 여기서 false가 나길 기대했지만 결과는 그렇지 않았고 첫번째 array(posts) 비교에서 false가 나왔습니다.

 

3번째 빨간 paragraph는 다시 theme change를 눌렀고 이는 정상적으로 array(posts) , tag1 비교를 수행한것을 확인할 수 있습니다.

 

왜 이런것인가요?

답변 2

·

답변을 작성해보세요.

1

새로운 배열이 의존성으로 들어가기 때문에 다른 값으로 인식합니다. 배열은 참조 값을 가지고 비교하기 때문입니다.

차근차근히 순서를 보시면요.

  1. Tag1을 클릭하면 tag 상태가 갱신되고 루트 컴포넌트가 리렌더됨

  2. Board 컴포넌트를 다시 그리면서 새로운 배열을 posts 인자로 전달. (객체 리터럴을 사용하기 때문)

  3. Board 컴포넌트는 인자 posts를 MyReact.useMemo 의존성 인자로 전달

  4. 이전에 받은 배열과 참조 값이 다름.

아래 AI 도 제대로 답해주셨네요.

1

인프런 AI 인턴님의 프로필

인프런 AI 인턴

2024.03.27

안녕하세요, 인프런 AI 인턴입니다.

dohyun_lim님의 질문에 대해 답변 드리겠습니다.

먼저, useMemo 훅은 성능 최적화를 위하여 의존성 배열(deps) 내의 값들이 변경될 경우에만 함수를 재실행하여 결과를 기억(memoize)합니다. 여기서 말씀하신 경우, 의존성 배열에 posts 배열과 tag가 포함되어 있습니다.

  1. “theme change” 버튼을 누르면 darkTheme 상태만 변경되고, posts 배열과 tag는 변경되지 않습니다. 따라서, useMemo에서는 의존성 배열의 변경이 없기 때문에 메모이제이션된 값을 재사용합니다. 이 경우, 모든 비교는 이전 값과 동일하므로 true가 됩니다.

  2. “Tag1” 버튼을 누르면 tag의 값이 변경되어야 하므로, useMemo 훅으로 인해 함수가 다시 실행되어야 합니다. 그러나 여러분이 관찰한 바와 같이, 예상과 달리 posts 배열의 비교에서 먼저 false가 발생했습니다. 이는 posts 배열이 객체(또는 배열)이기 때문에 참조에 의한 비교가 이루어지기 때문입니다. JavaScript에서는 객체의 참조가 다르면, 즉 객체가 메모리의 다른 주소를 가리키면, 이 두 객체는 서로 다르다고 판단됩니다. posts 배열이 새로운 배열로 전달되었거나, 배열 내부의 어떠한 요소가 변경되어 새로운 참조가 생성된 것으로 추정됩니다.

  3. 다시 “theme change” 버튼을 눌러도, tagposts의 값에 변동이 없기 때문에, 이전에 메모이제이션된 결과를 사용하게 됩니다.

결론적으로, posts 배열의 참조 비교에서 false가 나온 것은 배열이 새로운 참조를 가지게 변경되었기 때문에 발생합니다. React에서 배열이나 객체를 상태로 관리할 때, 상태가 변경될 경우 새로운 객체나 배열을 생성하여 상태를 업데이트하는 것이 일반적인데, 이 과정에서 배열의 참조가 변경되어 useMemo의 의존성 검사에서 false가 나온 것으로 보입니다.

이것이 귀하의 경우와 관련된 현상을 이해하는 데 도움이 되기를 바랍니다.