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

[6팀 안기환][Chapter 1-3] React, Beyond the Basics #41

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 46 additions & 2 deletions packages/assignment/src/@lib/equalities/deepEquals.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,48 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
/* eslint-disable @typescript-eslint/no-explicit-any */
export function deepEquals(objA: any, objB: any): boolean {
return objA === objB;
if (Object.is(objA, objB)) {
return true;
}

if (
typeof objA !== "object" ||
objA === null ||
typeof objB !== "object" ||
objB === null
) {
return false;
}

const keysA = Object.keys(objA);
const keysB = Object.keys(objB);

if (keysA.length !== keysB.length) {
return false;
}

const hasOwnProp = Object.prototype.hasOwnProperty;

for (let i = 0; i < keysA.length; i++) {
const currentKey = keysA[i];

if (!hasOwnProp.call(objB, currentKey)) {
return false;
}

const valueA = objA[currentKey];
const valueB = objB[currentKey];

const isObjectA = typeof valueA === "object" && valueA !== null;
const isObjectB = typeof valueB === "object" && valueB !== null;

if (isObjectA && isObjectB) {
if (!deepEquals(valueA, valueB)) {
return false;
}
} else if (!Object.is(valueA, valueB)) {
return false;
}
}

return true;
}
34 changes: 32 additions & 2 deletions packages/assignment/src/@lib/equalities/shallowEquals.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,34 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
/* eslint-disable @typescript-eslint/no-explicit-any */
export function shallowEquals(objA: any, objB: any): boolean {
return objA === objB;
if (Object.is(objA, objB)) {
return true;
}

if (
typeof objA !== "object" ||
objA === null ||
typeof objB !== "object" ||
objB === null
) {
return false;
}

const keysA = Object.keys(objA);
const keysB = Object.keys(objB);

if (keysA.length !== keysB.length) {
return false;
}

for (let i = 0; i < keysA.length; i++) {
const currentKey = keysA[i];
if (
!Object.hasOwnProperty.call(objB, currentKey) ||
!Object.is(objA[currentKey], objB[currentKey])
) {
return false;
}
}

return true;
}
20 changes: 16 additions & 4 deletions packages/assignment/src/@lib/hocs/memo.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { shallowEquals } from "../equalities";
import { ComponentType } from "react";
import { useRef } from "../hooks";

export function memo<P extends object>(
Component: ComponentType<P>,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
equals = shallowEquals
) {
return Component;
return function (currentProps: Record<string, string>) {
const prevProps = useRef<Record<string, string> | null>(null);
const prevComponent = useRef<ComponentType<P> | null>(null);

if (prevProps.current !== null && equals(prevProps.current, currentProps)) {
return prevComponent.current;
}

prevProps.current = currentProps;

prevComponent.current = (Component as any)(currentProps);

return prevComponent.current;
};
}
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 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
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
export function useCallback<T extends (...args: any[]) => any>(
factory: T,
deps: DependencyList
): T {
// eslint-disable-next-line react-hooks/exhaustive-deps
return useMemo(() => 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);
}
28 changes: 22 additions & 6 deletions packages/assignment/src/@lib/hooks/useMemo.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
import { DependencyList } from "react";
import { shallowEquals } from "../equalities";
import { useRef } from "./useRef";

// 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();
export function useMemo<T>(
factory: () => T,
deps: DependencyList,
equals = shallowEquals
): T {
const prevDepsRef = useRef<DependencyList | undefined>(undefined);
const memoizedValueRef = useRef<T | undefined>(undefined);

if (!deps) {
return factory();
}

if (prevDepsRef.current && equals(prevDepsRef.current, deps)) {
return memoizedValueRef.current as T;
}

const newValue = factory();
prevDepsRef.current = deps;
memoizedValueRef.current = newValue;

return newValue;
}
6 changes: 4 additions & 2 deletions packages/assignment/src/@lib/hooks/useRef.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { useState } from "react";

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