-
Notifications
You must be signed in to change notification settings - Fork 62
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
[7팀 김원표] [Chapter 1-3] React, Beyond the Basics #38
base: main
Are you sure you want to change the base?
Changes from 11 commits
1635c08
96eb1a9
bf31bb5
c2930f0
7c3e932
446d7bc
965ed4e
7a13512
2726ca6
9927641
2c98372
828c769
9907093
4ed92e9
633eaed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
npx lint-staged | ||
npm run tsc && npm run prettier:write && npm run lint:fix |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,35 @@ | ||
// deepEquals 함수는 두 값의 깊은 비교를 수행합니다. | ||
export function deepEquals<T>(objA: T, objB: T): boolean { | ||
return objA === objB; | ||
// 1. 기본 타입이거나 null인 경우 처리 | ||
if (objA === objB) return true; | ||
|
||
if ( | ||
typeof objA !== "object" || | ||
objA === null || | ||
typeof objB !== "object" || | ||
objB === null | ||
) { | ||
return false; | ||
} | ||
|
||
// 2. 둘 다 객체인 경우: | ||
// 배열인지 확인 | ||
if (Array.isArray(objA) !== Array.isArray(objB)) { | ||
return false; | ||
} | ||
|
||
// 객체의 키 개수가 다른 경우 처리 | ||
const keysA = Object.keys(objA) as (keyof T)[]; | ||
const keysB = Object.keys(objB) as (keyof T)[]; | ||
|
||
if (keysA.length !== keysB.length) return false; | ||
|
||
// 재귀적으로 각 속성에 대해 deepEquals 호출 | ||
for (const key of keysA) { | ||
if (!deepEquals(objA[key], objB[key])) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,32 @@ | ||
//shallowEquals 함수는 두 값의 얕은 비교를 수행합니다. | ||
export function shallowEquals<T>(objA: T, objB: T): boolean { | ||
return objA === objB; | ||
// 1. 두 값이 정확히 같은지 확인 (참조가 같은 경우) | ||
if (objA === objB) return true; | ||
|
||
// 2. 둘 중 하나라도 객체가 아닌 경우 처리 | ||
if ( | ||
typeof objA !== "object" || | ||
objA === null || | ||
typeof objB !== "object" || | ||
objB === null | ||
) { | ||
return false; | ||
} | ||
|
||
// 3. 객체의 키 개수가 다른 경우 처리 | ||
const keysA = Object.keys(objA) as (keyof T)[]; | ||
const keysB = Object.keys(objB) as (keyof T)[]; | ||
if (keysA.length !== keysB.length) return false; | ||
|
||
// 4. 모든 키에 대해 얕은 비교 수행 | ||
for (const key of keysA) { | ||
if ( | ||
!Object.prototype.hasOwnProperty.call(objB, key) || | ||
objA[key] !== objB[key] | ||
Comment on lines
+24
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 |
||
) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,24 @@ | ||
/* eslint-disable @typescript-eslint/no-unused-vars */ | ||
import { shallowEquals } from "../equalities"; | ||
import { ComponentType } from "react"; | ||
import React, { ComponentType } from "react"; | ||
import { useRef } from "../hooks"; | ||
|
||
// memo HOC는 컴포넌트의 props를 얕은 비교하여 불필요한 리렌더링을 방지합니다. | ||
export function memo<P extends object>( | ||
Component: ComponentType<P>, | ||
_equals = shallowEquals, | ||
) { | ||
return Component; | ||
const MemoizedComponent = (props: P) => { | ||
// 1. 이전 props를 저장할 ref 생성 | ||
const prevProps = useRef<P | null>(null); | ||
|
||
// 2. 메모이제이션된 컴포넌트 생성 | ||
// 3. equals 함수를 사용하여 props 비교 | ||
if (prevProps.current === null || !_equals(prevProps.current, props)) { | ||
prevProps.current = props; | ||
return React.createElement(Component, prevProps.current); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 .tsx로 변경하고 |
||
}; | ||
// 4. props가 변경된 경우에만 새로운 렌더링 수행 | ||
|
||
return MemoizedComponent; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,12 @@ | ||
/* eslint-disable @typescript-eslint/no-unused-vars,@typescript-eslint/no-unsafe-function-type */ | ||
/* eslint-disable @typescript-eslint/no-unsafe-function-type */ | ||
import { DependencyList } from "react"; | ||
import { useMemo } from "./useMemo"; | ||
|
||
export function useCallback<T extends Function>( | ||
factory: T, | ||
_deps: DependencyList, | ||
) { | ||
// 직접 작성한 useMemo를 통해서 만들어보세요. | ||
return factory as T; | ||
// 함수와 의존성 배열을 인자로 받고, 반환 값을 메모이제이션된 함수 | ||
return useMemo(() => factory, _deps); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,27 @@ | ||
/* eslint-disable @typescript-eslint/no-unused-vars */ | ||
import { DependencyList } from "react"; | ||
import { shallowEquals } from "../equalities"; | ||
import { useRef } from "./useRef"; | ||
|
||
// useMemo 훅은 계산 비용이 높은 값을 메모이제이션합니다. | ||
export function useMemo<T>( | ||
factory: () => T, | ||
_deps: DependencyList, | ||
_equals = shallowEquals, | ||
): T { | ||
// 직접 작성한 useRef를 통해서 만들어보세요. | ||
return factory(); | ||
// 1. 이전 의존성과 결과를 저장할 ref 생성 | ||
const ref = useRef<{ deps: DependencyList | null; value: T | null }>({ | ||
deps: null, | ||
value: null, | ||
}); | ||
|
||
Comment on lines
+13
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 이부분에 2개의 useRef를 선언했거든요! |
||
// 2. 현재 의존성과 이전 의존성 비교 | ||
if (ref.current?.deps === null || !_equals(ref.current?.deps, _deps)) { | ||
// 3. 의존성이 변경된 경우 factory 함수 실행 및 결과 저장 | ||
ref.current.value = factory(); | ||
ref.current.deps = _deps; | ||
} | ||
|
||
// 4. 메모이제이션된 값 반환 | ||
return ref.current?.value as T; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,9 @@ | ||
export function useRef<T>(initialValue: T): { current: T } { | ||
// useRef 훅은 렌더링 사이에 값을 유지하는 가변 ref 객체를 생성합니다. | ||
import { useState } from "react"; | ||
|
||
export function useRef<T>(initialValue: T | null): { current: T | null } { | ||
// React의 useState를 이용해서 만들어보세요. | ||
return { current: initialValue }; | ||
const [ref] = useState(() => ({ current: initialValue })); | ||
|
||
return ref; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
위에서 primitive 타입을 걸러내고 여기서 따로 null과 undefined를 찾는 방법이네요!
저와는 다른 방식으로 접근하셔서 좋은 인사이트를 얻었습니다!