FrontEnd/[리액트 2부] 고급 주제와 훅

[4.4장 메모이제이션 훅]

Tony Lim 2024. 3. 25. 11:05

useMemo는 일종의 캐시역할을 하는 훅이다.

  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];
    if (prevDeps.every((prev, index) => prev === nextDeps[index])) {
      cursor = cursor + 1;
      return prevValue;
    }

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

전달받은 deps (dependency) 가 변한 것이 있는지 체크하게 되고 없다면 이전에 캐싱한 값을 전달해주게된다.

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]);

  return (
    <>
      <div>
        <button onClick={() => setDarkTheme(!darkTheme)}>Theme Change</button>
        <span>{darkTheme ? "dark" : "light"}</span>
      </div>
      <ul>
        {filteredPosts.map(({ id, content, tag }) => {
          return (
            <li>
              {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}
      />
    </>
  );
};

실제로 theme이 변경이 되더라도 deps로 전달해준 posts, tag가 변하지 않았기 때문에 useMemo에 전달한 함수인filterPosts는 다시 호출이 되지 않고 기존에 캐싱한 값을 전달하게 된다.

https://www.inflearn.com/questions/1217391/4-4%EC%9E%A5-%EB%A9%94%EB%AA%A8%EC%9D%B4%EC%A0%9C%EC%9D%B4%EC%85%98-%ED%9B%85-4-4-2-usememo-%EC%97%90%EC%84%9C-every-%ED%95%A8%EC%88%98%EC%97%90-%EA%B4%80%ED%95%9C-%EC%A7%88%EB%AC%B8%EC%9E%85%EB%8B%88%EB%8B%A4

 

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

안녕하세요 선생님 every 배열을 비교하는 방법이 궁금해서 로그를 찍어보았습니다.// export default App;import MyReact from './lib/MyReact';import React from 'react';const Board = ({ posts...

www.inflearn.com


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]);

  const handleClick = MyReact.useMemo(() => (postId) => {
    console.log("handleClick", postId);
  }, []);


  console.log("Board rendered");

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

const FilteredPosts = MyReact.memo(({ value, onClick }) => {
  console.log("FilteredPosts rendered");
  return (
    <ul>
      {value.map(({ id, content, tag }) => {
        return (
          <li key={id} onClick={() => onClick(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}
      />
    </>
  );
};

FilteredPost에 props 인자로 onClick이 추가되었다. Board component를 생성할 때마다 FilteredPost에 handClick을 onClick props 인자로 전달해주는데

이때 기존에 useMemo를 사용했던 filteredPosts와는 다르게 handleClick은 매번 새로운 함수가 생성되어 들어오게 된다. 그렇게 되면 FilteredPosts가 제대로 캐싱된 component를 가져오지 못하고 매번 새롭게 생성되게 된다.

이를 방지하기 위해서 handleClick 또한 useMemo로 캐싱한다.
매번 같은 함수를 새로 생성해서 === 비교를 하면 다른 메모리주소를 가르킴 (다른 object) 임으로 false로 처리된다.

https://www.inflearn.com/questions/1218434/4-4%EC%9E%A5-%EB%A9%94%EB%AA%A8%EC%9D%B4%EC%A0%9C%EC%9D%B4%EC%85%98-%ED%9B%85-4-4-4-usecallback-curried-function%EC%97%90-%EA%B4%80%ED%95%9C-%EC%A7%88%EB%AC%B8%EC%9E%85%EB%8B%88%EB%8B%A4

 

[4.4장 메모이제이션 훅] 4.4.4 useCallback curried function에 관한 질문입니다. - 인프런

안녕하세요 선생님 질문이 2개 있습니다. const handleClick1 = MyReact.useMemo((postId) => { console.log('handleClick', postId); }, []); const handleClick2 = MyRe...

www.inflearn.com

이때 useMemo를 쓸때 그냥 arrow function을 전달해주는게 아니라 강의 예시에서는 curried function (double arrow fucntion)을 전달해주었다. 이에 대한 차이를 물어보는 질문이다.