Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8팀 채예린] [Chapter 1-3] React, Beyond the Basics #35

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
34 changes: 33 additions & 1 deletion packages/assignment/src/@lib/equalities/deepEquals.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,36 @@
/**
* 두 값을 깊게 비교하는 함수
* @param objA 첫 번째 비교 대상 값
* @param objB 두 번째 비교 대상 값
* @returns 두 값이 깊은 비교에서 동일하면 true, 그렇지 않으면 false
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function deepEquals(objA: any, objB: any): boolean {
return objA === objB;
// 1. 두 값이 같은 참조를 가지거나 값이 같으면 true 반환
if (objA === objB) return true;

// 2. 하나라도 객체가 아니거나 null인 경우 false 반환
// (null의 typeof는 'object'지만 원시값이므로 따로 처리)
if (
typeof objA !== "object" ||
typeof objB !== "object" ||
objA === null ||
objB === null
) {
return false;
}

// 3. 객체의 키 배열을 구하고, 키 개수가 다르면 false 반환
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) return false;

// 4. 모든 키에 대해 값에 대해 재귀적으로 깊은 비교
for (const key of keysA) {
// - 다른 경우가 하나라도 있으면 false 반환
if (!deepEquals(objA[key], objB[key])) return false;
}

// - 모든 키에 대해 값이 같으면 true 반환
return true;
}
34 changes: 33 additions & 1 deletion packages/assignment/src/@lib/equalities/shallowEquals.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,36 @@
/**
* 두 값을 얕게 비교하는 함수
* @param objA 첫 번째 비교 대상 값
* @param objB 두 번째 비교 대상 값
* @returns 두 값이 얕은 비교에서 동일하면 true, 그렇지 않으면 false
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function shallowEquals(objA: any, objB: any): boolean {
return objA === objB;
// 1. 두 값이 같은 참조를 가지거나 값이 같으면 true 반환
if (objA === objB) return true;

// 2. 하나라도 객체가 아니거나 null인 경우 false 반환
// (null의 typeof는 'object'지만 원시값이므로 따로 처리)
if (
typeof objA !== "object" ||
typeof objB !== "object" ||
objA === null ||
objB === null
) {
return false;
}

// 3. 객체의 키 배열을 구하고, 키 개수가 다르면 false 반환
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) return false;

// 4. 모든 키에 대해 값을 비교 (첫번째 depth만)
for (const key of keysA) {
// - 다른 경우가 하나라도 있으면 false 반환
if (objA[key] !== objB[key]) return false;
}

// - 모든 키에 대해 값이 같으면 true 반환
return true;
}
6 changes: 2 additions & 4 deletions packages/assignment/src/@lib/hocs/deepMemo.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { deepEquals } from "../equalities";
import { ComponentType } from "react";
import { memo } from "./memo.ts";
import { memo } from "./memo";

export function deepMemo<P extends object>(
Component: ComponentType<P>,
) {
export function deepMemo<P extends object>(Component: ComponentType<P>) {
return memo(Component, deepEquals);
}
12 changes: 0 additions & 12 deletions packages/assignment/src/@lib/hocs/memo.ts

This file was deleted.

27 changes: 27 additions & 0 deletions packages/assignment/src/@lib/hocs/memo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ReactNode, ComponentType } from "react";
import { shallowEquals } from "../equalities";
import { useRef } from "../hooks";

export function memo<P extends object>(
Component: ComponentType<P>,
equals = shallowEquals
) {
return (props: P) => {
// 1. 이전 props와 렌더링 결과를 저장할 ref 생성
const memoizedRef = useRef<{ props?: P; element?: ReactNode }>({});

// 2. 새로운 props와 렌더링 결과를 저장한다
// - 아직 초기화 되지 않은 경우
// - props가 변경된 경우
if (
!memoizedRef.current.props ||
!equals(props, memoizedRef.current.props)
) {
memoizedRef.current.props = props;
memoizedRef.current.element = <Component {...props} />;
}

// 3. 렌더링 결과를 반환한다
return memoizedRef.current.element;
};
}
14 changes: 8 additions & 6 deletions packages/assignment/src/@lib/hooks/useCallback.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { DependencyList } from "react";
import { useMemo } from "./useMemo";

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-explicit-any
export function useCallback<T extends (...args: any[]) => any>(factory: T, deps: DependencyList): T {
// 직접 작성한 useMemo를 통해서 만들어보세요.
return ((...args) => factory(...args)) as T
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function useCallback<T extends (...args: any) => any>(
factory: T,
deps: DependencyList
): T {
// eslint-disable-next-line react-hooks/exhaustive-deps
return useMemo<T>(() => factory, deps);
}
7 changes: 2 additions & 5 deletions packages/assignment/src/@lib/hooks/useDeepMemo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import { DependencyList } from "react";
import { useMemo } from "./useMemo";
import { deepEquals } from "../equalities";

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore

export function useDeepMemo<T>(factory: () => T, deps: DependencyList): T {
// 직접 작성한 useMemo를 참고해서 만들어보세요.
return useMemo(factory, deps, deepEquals)
// eslint-disable-next-line react-hooks/exhaustive-deps
return useMemo(factory, deps, deepEquals);
}
29 changes: 23 additions & 6 deletions packages/assignment/src/@lib/hooks/useMemo.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
import { DependencyList } from "react";
import { useRef } from "./useRef";
import { shallowEquals } from "../equalities";

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function useMemo<T>(factory: () => T, deps: DependencyList, equals = shallowEquals): T {
// 직접 작성한 useRef를 통해서 만들어보세요.
return factory();
// useMemo 훅은 계산 비용이 높은 값을 메모이제이션합니다.
export function useMemo<T>(
factory: () => T,
deps: DependencyList,
equals = shallowEquals
): T {
// 1. 이전 의존성과 결과를 저장할 ref 생성
const memoizedRef = useRef<{
result?: T;
deps?: DependencyList;
}>({});

// 2. 새로운 의존성과 결과를 저장한다
// - 아직 초기화 되지 않은 경우
// - 의존성이 변경된 경우
if (!memoizedRef.current.deps || !equals(memoizedRef.current.deps, deps)) {
memoizedRef.current.result = factory();
memoizedRef.current.deps = deps;
}

// 4. 메모이제이션된 값 반환
return memoizedRef.current.result as T;
}
7 changes: 5 additions & 2 deletions packages/assignment/src/@lib/hooks/useRef.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { useState } from "react";

export function useRef<T>(initialValue: T): { current: T } {
// React의 useState를 이용해서 만들어보세요.
return { current: initialValue };
// React의 useState를 이용하여 useRef 를 구현
const [ref] = useState({ current: initialValue });
return ref;
}
Loading