-
Notifications
You must be signed in to change notification settings - Fork 56
[6팀 조영민] Chapter 1-3. React, Beyond the Basics #43
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
base: main
Are you sure you want to change the base?
Changes from 2 commits
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,3 +1,24 @@ | ||
| export const deepEquals = (a: unknown, b: unknown) => { | ||
| import { isObject } from "./utils"; | ||
|
|
||
| export const deepEquals = (a: unknown, b: unknown): boolean => { | ||
| if (Array.isArray(a) && Array.isArray(b)) { | ||
| if (a.length !== b.length) { | ||
| return false; | ||
| } | ||
|
|
||
| return a.every((item, index) => deepEquals(item, b[index])); | ||
| } | ||
|
|
||
| if (isObject(a) && isObject(b)) { | ||
| const aKeys = Object.keys(a); | ||
| const bKeys = Object.keys(b); | ||
|
|
||
| if (aKeys.length !== bKeys.length) { | ||
| return false; | ||
| } | ||
|
|
||
| return aKeys.every((key) => deepEquals(a[key], b[key])); | ||
| } | ||
|
|
||
| return a === b; | ||
| }; | ||
|
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. 오 every()를 사용하셨네요 ! 전 for 루프를 통해 구현했는데 이 방법도 잘 알아갑니다. ㅎㅎ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,24 @@ | ||
| import { isObject } from "./utils"; | ||
|
|
||
| export const shallowEquals = (a: unknown, b: unknown) => { | ||
| if (Array.isArray(a) && Array.isArray(b)) { | ||
| if (a.length !== b.length) { | ||
| return false; | ||
| } | ||
|
|
||
| return a.every((el, index) => el === b[index]); | ||
| } | ||
|
|
||
| if (isObject(a) && isObject(b)) { | ||
| const aKeys = Object.keys(a); | ||
| const bKeys = Object.keys(b); | ||
|
|
||
| if (aKeys.length !== bKeys.length) { | ||
| return false; | ||
| } | ||
|
|
||
| return aKeys.every((key) => a[key] === b[key]); | ||
| } | ||
|
|
||
| return a === b; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| export const isObject = (value: unknown): value is Record<string, unknown> => { | ||
| return typeof value === "object" && value !== null && !Array.isArray(value); | ||
| }; | ||
|
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. isObject를 별도의 메소드 없이 직접 구현하셨는데 엣지 케이스가 존재할 수 있을 것 같아요! 이 부분 리팩토링 하실 떄 한번 더 생각해 보시는 건 어떨까요?? |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| import type { FunctionComponent } from "react"; | ||
| import { deepEquals } from "../equals"; | ||
| import { memo } from "./memo"; | ||
|
|
||
| export function deepMemo<P extends object>(Component: FunctionComponent<P>) { | ||
| return Component; | ||
| return memo(Component, deepEquals); | ||
| } |
|
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. useRef를 사용하면 인스턴스별로 props 상태를 안전하게 유지할 수 있을 것 같아요. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| /* eslint-disable @typescript-eslint/no-unused-vars,@typescript-eslint/no-unsafe-function-type */ | ||
| /* eslint-disable @typescript-eslint/no-unsafe-function-type */ | ||
| import type { 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); | ||
| } |
|
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. initialized로 초기 렌더 여부를 명시해준 점이 가독성과 추후 유지보수에 도움이 될 것 같네요. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,23 @@ | ||
| /* eslint-disable @typescript-eslint/no-unused-vars */ | ||
| import type { DependencyList } from "react"; | ||
| import { useRef } from "./useRef"; | ||
| import { shallowEquals } from "../equals"; | ||
|
|
||
| export function useMemo<T>(factory: () => T, _deps: DependencyList, _equals = shallowEquals): T { | ||
| // 직접 작성한 useRef를 통해서 만들어보세요. | ||
| return factory(); | ||
| const ref = useRef<{ | ||
| deps: DependencyList; | ||
| value: T | undefined; | ||
| initialized: boolean; | ||
| }>({ | ||
| deps: [], | ||
| value: undefined, | ||
| initialized: false, | ||
| }); | ||
|
|
||
| if (!ref.current.initialized || !_equals(ref.current.deps, _deps)) { | ||
| ref.current.value = factory(); | ||
| ref.current.deps = _deps; | ||
| ref.current.initialized = true; | ||
| } | ||
|
Comment on lines
5
to
+20
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. value의 초기값으로 factory 함수를 넣게 된다면 initialized 값이 없어져도 되지않을까 생각이 듭니다. |
||
|
|
||
| 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 } { | ||
| // useState를 이용해서 만들어보세요. | ||
| return { current: initialValue }; | ||
| import { useState } from "react"; | ||
|
|
||
| type Ref<T> = { current: T }; | ||
|
|
||
| export function useRef<T>(initialValue: T): Ref<T> { | ||
| const [ref] = useState<Ref<T>>(() => ({ current: initialValue })); | ||
|
|
||
| return ref; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,19 @@ | ||
| import { useState } from "react"; | ||
| import { shallowEquals } from "../equals"; | ||
| import { useCallback } from "./useCallback"; | ||
|
|
||
| export const useShallowState = <T>(initialValue: Parameters<typeof useState<T>>[0]) => { | ||
| // useState를 사용하여 상태를 관리하고, shallowEquals를 사용하여 상태 변경을 감지하는 훅을 구현합니다. | ||
| return useState(initialValue); | ||
| export const useShallowState = <T>(initialValue: T | (() => T)) => { | ||
| const [state, setState] = useState<T>(initialValue); | ||
|
|
||
| const setShallowState = useCallback((next: T) => { | ||
| setState((prev) => { | ||
| if (shallowEquals(prev, next)) { | ||
| return prev; | ||
| } | ||
|
|
||
| return next; | ||
| }); | ||
| }, []); | ||
|
|
||
| return [state, setShallowState] as const; | ||
| }; |
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.
1depth 비교만 해도 충분한 함수 또는 객체가 재귀적으로 deepEquals 함수로 호출될 것 같은데, 성능최적화를 고려해서 1depth일 때는 early return 또는 shallowEquals 함수로 비교하고, 2depth 이상 일 때만 재귀적으로 deepEquals 함수를 호출하는 방안으로 추후 개선해보시면 어떨까요~?