항해99 플러스 프론트엔드 5기 3주차
항해99 플러스 프론트엔드 5기 3주차
이번주 발제는 react의 기본 hook을 직접 구현하면서 어떤 원리로 동작하는지 파악해보기 입니다
shallowEquals
shallowEquals.ts 를 먼저 구현해야하는데
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const isObject = (obj: unknown): obj is Record<string, unknown> => {
return obj !== null && typeof obj === "object";
};
export function shallowEquals<T>(objA: T, objB: T): boolean {
if (objA === objB) {
return true;
}
if (Array.isArray(objA) && Array.isArray(objB)) {
if (objA.length !== objB.length) {
return false;
}
return objA.every((value, key) => objB[key] === value);
}
if (isObject(objA) && isObject(objB)) {
const entriesA = Object.entries(objA);
const entriesB = Object.entries(objB);
return (
entriesA.length === entriesB.length &&
entriesA.every(([key, value]) => {
return objB[key] === value;
})
);
}
return false;
}
isObject 타입 가드로, null 방지랑 비교를 해주고
objA === objB return true 같은 값이면 바로 통과
Array 비교시 every로 순서대로 모든 요소 비교
Object 비교시, key/value pair를 비교
deepEquals
이건 이제 깊은 비교인데요, 발제때 shallowEquals에서 조금만 바꾸면 된다는 힌트를 주셨고, deepEquals 자체가 iterative하게 모든 내부 값을 비교하는것 이였습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const isObject = (obj: unknown): obj is Record<string, unknown> => {
return obj !== null && typeof obj === "object";
};
export function deepEquals<T>(objA: T, objB: T): boolean {
if (objA === objB) {
return true;
}
if (Array.isArray(objA) && Array.isArray(objB)) {
if (objA.length !== objB.length) {
return false;
}
return objA.every((value, index) => deepEquals(value, objB[index]));
}
if (isObject(objA) && isObject(objB)) {
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) return false;
return keysA.every((key) => deepEquals(objA[key], objB[key]));
}
return false;
}
iterative 하게 진행될수있게 deepEquals를 안에다가 넣어줍니다.
useRef
발제 때 주신 답으로 진행했습니다
1
2
3
4
5
6
7
import {useState} from "react";
export function useRef<T>(initialValue: T): { current: T } {
// React의 useState를 이용해서 만들어보세요.
const [value] = useState({current: initialValue});
return value;
}
useRef는 rerender를 유발하지 않으면서 .current를 가진 object를 return 합니다.
useState는 최초 1회만 초기값 함수 또는 값을 실행하는데요 [value]로 만 가져오고 setValue는 쓰지 않아 값은 유지, { current: T } object는 rendering간에 동일한 참조로 유지되어 .current를 수정해도 rerendering이 발생하지 않습니다.
useMemo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import {DependencyList} from "react";
import {shallowEquals} from "../equalities";
import {useRef} from "./useRef.ts";
export function useMemo<T>(
factory: () => T,
_deps: DependencyList,
_equals = shallowEquals,
): T {
// 직접 작성한 useRef를 통해서 만들어보세요.
const memoRef = useRef<{ _deps: DependencyList; value: T } | null>(null);
if (memoRef.current === null || !_equals(memoRef.current._deps, _deps)) {
memoRef.current = {
_deps: _deps,
value: factory(),
};
}
return memoRef.current.value;
}
memoRef를 통해 useRef{ _deps, value } 구조로 이전 값을 기억, dependent array가 바뀌었을때만, factory()를 실행합니다!
This post is licensed under CC BY 4.0 by the author.