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 #31

Open
wants to merge 15 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
35 changes: 34 additions & 1 deletion packages/assignment/src/@lib/equalities/deepEquals.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,37 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function deepEquals(objA: any, objB: any): boolean {
return objA === objB;
// 1. 기본 타입이거나 null인 경우 처리
if (objA === objB) {
return true
}

// null 비교
if (objA === null || objB === null) {
return objA === objB
}

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

// 2. 둘 다 객체인 경우:
// - 객체의 키 개수가 다른 경우 처리
if (Object.keys(objA).length !== Object.keys(objB).length) {
return false
}

// - 재귀적으로 각 속성에 대해 deepEquals 호출
for (const [aKey, aValue] of Object.entries(objA)) {
// objB에 objA의 속성이 없거나 값이 다르면 false
if (!(aKey in objB)) {
return false
}

if (!deepEquals(aValue, objB[aKey])) {
return false
}
}

// 이 부분을 적절히 수정하세요.
return true
}
35 changes: 34 additions & 1 deletion packages/assignment/src/@lib/equalities/shallowEquals.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,37 @@
import { J } from "vitest/dist/chunks/reporters.DAfKSDh5.js"

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function shallowEquals(objA: any, objB: any): boolean {
return objA === objB;
// 1. 두 값이 정확히 같은지 확인 (참조가 같은 경우)
if (objA === objB) {
return true
}

// 2. 둘 중 하나라도 객체가 아닌 경우 처리, null의 type은 object이므로 따로 처리해줘야 함
if (objA === null || objB === null) {
return objA === objB
}

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

// 3. 객체의 키 개수가 다른 경우 처리
if (Object.keys(objA).length !== Object.keys(objB).length) {
return false
}

// 4. 모든 키에 대해 얕은 비교 수행
for (const [aKey, aValue] of Object.entries(objA)) {
// objB에 objA의 속성이 없거나 값이 다르면 false
if (!(aKey in objB)) {
return false
}

if (objB[aKey] !== aValue) {
return false
}
}

return true
}
20 changes: 17 additions & 3 deletions packages/assignment/src/@lib/hocs/memo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { shallowEquals } from "../equalities";
import { ComponentType } from "react";
import { shallowEquals } from "../equalities"
import { ComponentType, createElement } from "react"
import { useRef } from "../hooks/useRef"

export function memo<P extends object>(
Component: ComponentType<P>,
Expand All @@ -8,5 +9,18 @@ export function memo<P extends object>(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
equals = shallowEquals
) {
return Component;
return (props: P) => {
// 저장할 ref
const prevProps = useRef<P | null>(null)
const prevComponent = useRef<JSX.Element | null>(null)

// ref가 없거나, props가 ref와 다르다면
if (!equals(props, prevProps.current)) {
// ref에 props를 저장
prevProps.current = props
prevComponent.current = createElement(Component, props)
}

return prevComponent.current
}
}
12 changes: 9 additions & 3 deletions packages/assignment/src/@lib/hooks/useCallback.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { DependencyList } from "react";
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 {
export function useCallback<T extends (...args: any[]) => any>(
factory: T,
deps: DependencyList
): T {
// 직접 작성한 useMemo를 통해서 만들어보세요.
return ((...args) => factory(...args)) as T

//실제로 useCallback(fn, deps)은 useMemo(() => fn, deps)와 같다.
return useMemo(() => factory, deps)
}
18 changes: 15 additions & 3 deletions packages/assignment/src/@lib/hooks/useMemo.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import { DependencyList } from "react";
import { shallowEquals } from "../equalities";
import { DependencyList, useRef } from "react"
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();

const ref = useRef<{
deps: DependencyList | undefined
value: T | undefined
}>({ deps: undefined, value: undefined })

// ref deps가 없거나 , deps가 ref deps와 다르다면
if (!ref.current.deps || !equals(ref.current.deps, deps)) {
ref.current.value = factory()
ref.current.deps = deps
}

return ref.current.value as T
}
7 changes: 6 additions & 1 deletion packages/assignment/src/@lib/hooks/useRef.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { useState } from "react"

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

const [ref] = useState<{ current: T }>({ current: initialValue })

return ref
}
Loading