From 00a56d869392a21b47a07ab44cada165c75fec4c Mon Sep 17 00:00:00 2001 From: bongjoonkim Date: Tue, 31 Dec 2024 21:34:49 +0900 Subject: [PATCH] =?UTF-8?q?memo.ts=EA=B0=80=20=EC=A0=9C=EB=8C=80=EB=A1=9C?= =?UTF-8?q?=20=EC=85=8B=ED=8C=85=EB=90=98=EC=A7=80=20=EC=95=8A=EC=95=98?= =?UTF-8?q?=EB=8D=98=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/@lib/equalities/deepEquals.ts | 54 ++++++---------------------- src/@lib/equalities/shallowEquals.ts | 13 ++++--- src/@lib/hocs/deepMemo.ts | 1 + src/@lib/hocs/memo.ts | 22 ++++++------ src/App.tsx | 4 +-- 5 files changed, 34 insertions(+), 60 deletions(-) diff --git a/src/@lib/equalities/deepEquals.ts b/src/@lib/equalities/deepEquals.ts index d8fdefd..f38cd75 100644 --- a/src/@lib/equalities/deepEquals.ts +++ b/src/@lib/equalities/deepEquals.ts @@ -1,69 +1,37 @@ -// export function deepEquals(objA: T, objB: T): boolean { -// if (typeof objA === "object" && typeof objB === "object") { -// if (objA == null && objB == null) { -// return true; -// } -// if (Object.keys(objA).length === Object.keys(objB).length) { -// for (const key of Object.keys(objA)) { -// if (typeof objA[key] === "object" && typeof objB[key] === "object") { -// return deepEquals(objA[key], objB[key]); -// } -// if (objA[key] !== objB[key]) { -// return false; -// } -// } -// return true; -// } else { -// return false; -// } -// } else { -// return objA === objB; -// } -// } - +// 깊은 비교 export function deepEquals(objA: T, objB: T): boolean { - // 기본 타입이거나 null/undefined인 경우 + // 원시 타입이 같은 경우 확실한 true이다. if (objA === objB) { return true; } - - // null/undefined 체크 - if (objA == null || objB == null) { - return false; - } - - // 배열 체크 - if (Array.isArray(objA) && Array.isArray(objB)) { - if (objA.length !== objB.length) { - return false; - } - for (let i = 0; i < objA.length; i++) { - if (!deepEquals(objA[i], objB[i])) { - return false; - } - } - return true; + // 하나라도 null이 있을 경우 + if (objA === null || objB === null) { + return objA === objB; } - // 객체 체크 + // 타입이 객체인 경우(배열 포함) if (typeof objA === "object" && typeof objB === "object") { const keysA = Object.keys(objA); const keysB = Object.keys(objB); + // 키 길이가 같은 경우 return false; if (keysA.length !== keysB.length) { return false; } for (const key of keysA) { + // keysB에 keysA에 있는 키가 없는 경우 if (!keysB.includes(key)) { return false; } + // 항상 objA[key], objB[key]를 재귀적으로 deepEquals를 해주는게 포인트 if (!deepEquals(objA[key], objB[key])) { return false; } } + // key와 value가 모두 같다면 return true return true; } - + // 어떠한 경우도 아니면 false return false; } \ No newline at end of file diff --git a/src/@lib/equalities/shallowEquals.ts b/src/@lib/equalities/shallowEquals.ts index 04a5220..8fdca65 100644 --- a/src/@lib/equalities/shallowEquals.ts +++ b/src/@lib/equalities/shallowEquals.ts @@ -1,25 +1,30 @@ +// 얕은 비교 export function shallowEquals(objA: T, objB: T): boolean { // 두 값이 정확히 같은지 확인(참조가 같은 경우) if (objA === objB) { return true; } - + // 하나라고 null이면 false이다. 둘 다 null인 경우는 위의 if문에서 처리됨 if (objA === null || objB === null) { - return objA === objB; + return false; } + // 둘 다 객체인 경우(배열 포함) if (typeof objA === "object" && typeof objB === "object") { - if (Object.keys(objA).length !== Object.keys(objB).length) { + const objAKeys = Object.keys(objA); + const objBKeys = Object.keys(objB); + if (objAKeys.length !== objBKeys.length) { return false; } else { - for (const key of Object.keys(objA)) { + for (const key of objAKeys) { if (objA[key] !== objB[key]) { return false; } } return true; } + // 모두 아닌 경우 } else { return objA === objB; } diff --git a/src/@lib/hocs/deepMemo.ts b/src/@lib/hocs/deepMemo.ts index dae45d2..4923bec 100644 --- a/src/@lib/hocs/deepMemo.ts +++ b/src/@lib/hocs/deepMemo.ts @@ -2,6 +2,7 @@ import { deepEquals } from "../equalities"; import { ComponentType } from "react"; import { memo } from "./memo.ts"; +// 깊은 메모 export function deepMemo

(Component: ComponentType

) { return memo(Component, deepEquals); } diff --git a/src/@lib/hocs/memo.ts b/src/@lib/hocs/memo.ts index 94acd2c..b09d97f 100644 --- a/src/@lib/hocs/memo.ts +++ b/src/@lib/hocs/memo.ts @@ -1,22 +1,22 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { shallowEquals } from "../equalities"; -import { ComponentType } from "react"; +import {ComponentType, createElement, ReactNode, useEffect} from "react"; +import {useRef} from "../hooks"; + export function memo

( Component: ComponentType

, _equals = shallowEquals, ) { - let oldProps = null; - let prevResult = null; return (props) => { - if (!oldProps || !_equals(oldProps, props)) { - prevResult = Component(props); - } + // 렌더링이 되어도 기존 데이터(prev)를 가지고 있어야 하기 때문에 ref 사용 + const prevPropsRef = useRef

(null); + const prevResultRef = useRef(null); - // 현재 props를 이전 props로 저장 - oldProps = props; - - // 메모이제이션된 결과 반환 - return prevResult; + if (!prevPropsRef.current || !_equals(prevPropsRef.current, props)) { + prevResultRef.current = createElement(Component, props); + } + prevPropsRef.current = props; + return prevResultRef.current; } } diff --git a/src/App.tsx b/src/App.tsx index 4f06879..f74ec47 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ import React, {useState, createContext, useContext, useEffect} from "react"; import { generateItems, renderLog } from "./utils"; -import { useCallback, useMemo } from "./@lib"; -import {memo} from "react"; +import {memo, useCallback, useMemo } from "./@lib"; +// import {memo} from "react"; import {deepEquals} from "./@lib"; // 타입 정의