해결된 질문
작성
·
237
·
수정됨
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
새로운 배열이 의존성으로 들어가기 때문에 다른 값으로 인식합니다. 배열은 참조 값을 가지고 비교하기 때문입니다.
차근차근히 순서를 보시면요.
Tag1을 클릭하면 tag 상태가 갱신되고 루트 컴포넌트가 리렌더됨
Board 컴포넌트를 다시 그리면서 새로운 배열을 posts 인자로 전달. (객체 리터럴을 사용하기 때문)
Board 컴포넌트는 인자 posts를 MyReact.useMemo 의존성 인자로 전달
이전에 받은 배열과 참조 값이 다름.
아래 AI 도 제대로 답해주셨네요.
1
안녕하세요, 인프런 AI 인턴입니다.
dohyun_lim님의 질문에 대해 답변 드리겠습니다.
먼저, useMemo
훅은 성능 최적화를 위하여 의존성 배열(deps) 내의 값들이 변경될 경우에만 함수를 재실행하여 결과를 기억(memoize)합니다. 여기서 말씀하신 경우, 의존성 배열에 posts
배열과 tag
가 포함되어 있습니다.
“theme change” 버튼을 누르면 darkTheme
상태만 변경되고, posts
배열과 tag
는 변경되지 않습니다. 따라서, useMemo
에서는 의존성 배열의 변경이 없기 때문에 메모이제이션된 값을 재사용합니다. 이 경우, 모든 비교는 이전 값과 동일하므로 true가 됩니다.
“Tag1” 버튼을 누르면 tag
의 값이 변경되어야 하므로, useMemo
훅으로 인해 함수가 다시 실행되어야 합니다. 그러나 여러분이 관찰한 바와 같이, 예상과 달리 posts
배열의 비교에서 먼저 false가 발생했습니다. 이는 posts
배열이 객체(또는 배열)이기 때문에 참조에 의한 비교가 이루어지기 때문입니다. JavaScript에서는 객체의 참조가 다르면, 즉 객체가 메모리의 다른 주소를 가리키면, 이 두 객체는 서로 다르다고 판단됩니다. posts
배열이 새로운 배열로 전달되었거나, 배열 내부의 어떠한 요소가 변경되어 새로운 참조가 생성된 것으로 추정됩니다.
다시 “theme change” 버튼을 눌러도, tag
와 posts
의 값에 변동이 없기 때문에, 이전에 메모이제이션된 결과를 사용하게 됩니다.
결론적으로, posts
배열의 참조 비교에서 false가 나온 것은 배열이 새로운 참조를 가지게 변경되었기 때문에 발생합니다. React에서 배열이나 객체를 상태로 관리할 때, 상태가 변경될 경우 새로운 객체나 배열을 생성하여 상태를 업데이트하는 것이 일반적인데, 이 과정에서 배열의 참조가 변경되어 useMemo의 의존성 검사에서 false가 나온 것으로 보입니다.
이것이 귀하의 경우와 관련된 현상을 이해하는 데 도움이 되기를 바랍니다.