- useMemo 활용하기
참고:
|
<DayOfWeekPickerContext.Provider |
|
value={{ |
|
value: value || defaultDay, |
|
completed: completed || Array.from(Array(DAYS_IN_WEEK), () => false), |
|
onItemClick: useCallback(setValue, [setValue]), |
|
disabledDays: disabledDays || [], |
|
errorDays, |
|
}} |
|
> |
Provider의 value에 객체 형태로 넣다보니 렌더 때마다 새로운 객체가 생성될 것 같습니다. 그래서 useMemo를 이용해서 의존값이 모두 같으면 이전 객체를 사용할 수 있게 useMemo를 활용하는 방법이 있을 것 같습니다.
또한 기존엔 setValue 부분에 useCallback을 쓰셨는데, jsx 내부에서 리액트 훅을 쓰는 건 규칙 위반이라 제외했습니다.
<예시>
const contextValue = useMemo(
() => ({
value: value || defaultDay,
completed: completed || EMPTY_COMPLETED,
onItemClick: setValue,
disabledDays: disabledDays || EMPTY_DISABLED_DAYS,
errorDays,
}),
[value, defaultDay, completed, setValue, disabledDays, errorDays],
);
return (
// [변경 4] 객체 리터럴 대신 memo된 변수를 전달
<DayOfWeekPickerContext.Provider value={contextValue}>
<DayOfWeekPickerImpl ref={ref} {...commonProps} />
</DayOfWeekPickerContext.Provider>
);
- Context 분리 방법
자주 바뀌는 Context 와 자주 바뀌지 않는 Context를 따로 관리하고, 따로 구독하게 만들 수도 있습니다. 이를 통해 호출
참고:
|
import { createContext, Dispatch } from "react"; |
|
|
|
import { Days } from "./Days"; |
|
|
|
type DayOfWeekPickerContext = { |
|
value?: Days; |
|
completed: boolean[]; |
|
onItemClick: Dispatch<React.SetStateAction<Days | undefined>>; |
|
disabledDays?: Days[]; |
|
errorDays?: boolean[]; |
|
}; |
|
|
|
const DayOfWeekPickerContext = createContext<DayOfWeekPickerContext | null>(null); |
|
|
|
export default DayOfWeekPickerContext; |
간단하게 타입 지정 부분만 보여주면 다음과 같을 것 같네요.
type ValueContextType = {
value: Days;
};
type ConfigContextType = {
onItemClick: Dispatch<React.SetStateAction<Days | undefined>>;
completed: boolean[];
disabledDays: Days[];
errorDays?: boolean[];
};
const ValueContext = createContext<ValueContextType | null>(null);
const ConfigContext = createContext<ConfigContextType | null>(null);
다만 현재는 이렇게 해도 차이가 없을 것 같은데요. 그 이유는 1) Provider를 감싸고 있는 부분이 children 으로 받고 있는 것이 아니라 바로 자식으로 받고 있는 형태라 부모가 리렌더링되면서 자식도 모두 리렌더링되어 최적화 시도와 상관없이 자식 컴포넌트가 모두 리렌더링됨. 2) DayOfWeekPickerItem 컴포넌트에서 valueContext와 configContext를 모두 사용하기 때문에 분리해도 어차피 모두 사용하는 구조임
- useDayOfWeekPickerContext를 DayOfWeekPickerItem 컴포넌트가 아니라 그 부모인 DayOfWeekPickerImpl 에서 호출하고, DayOfWeekPickerItem는 React.memo 로 컴포넌트 리렌더링 최적화하기.
참고:
|
const DayOfWeekPickerItem = forwardRef<HTMLDivElement, DayOfWeekPickerItemProps>( |
|
({ day, children }, ref) => { |
|
const { value, onItemClick, completed, disabledDays, errorDays } = useDayOfWeekPickerContext(); |
|
const isCurrent = value === day; |
|
const isDisabled = disabledDays?.includes(day); |
이건 예시 코드를 짜려니 좀 많이 바꿔야할 것 같아서 작성하지 못했습니다.
다만 합성 컴포넌트는 기본적으로 "독립적으로 동작"하게 하기 위한 목적도 있는데 그 목적은 깨지게 되고 필수적으로 DayOfWeekPickerImpl에 의존하게 됩니다. 또한 props 의 참조값을 잘 관리해야 최적화가 잘 되기 때문에 신경을 잘 써야할 것 같습니다.
- zustand는 어떻게 하는지 봤더니 외부에서 변경된 값을 안전하게 읽게 하는 useSyncExternalStore 와 같은 훅을 사용하고 있습니다. 그리고 이 훅은 컴포넌트에서 구독하는 값이 달라지는 경우만 리렌더링하기 때문에 만약 월요일부터 일요일까지 중 요일을 선택했을 때, 월요일을 선택 시 기존에 선택됐던 요일(ex. 화요일)이 취소되어 월요일, 화요일만 다시 변경되고 다른 요일은 그대로 유지될 수 있습니다.
참고: https://github.com/pmndrs/zustand/blob/main/src/react.ts
참고:
FitLink-FE/packages/ui/src/components/DayOfWeekPicker/index.tsx
Lines 48 to 56 in 4843435
Provider의 value에 객체 형태로 넣다보니 렌더 때마다 새로운 객체가 생성될 것 같습니다. 그래서 useMemo를 이용해서 의존값이 모두 같으면 이전 객체를 사용할 수 있게 useMemo를 활용하는 방법이 있을 것 같습니다.
또한 기존엔 setValue 부분에 useCallback을 쓰셨는데, jsx 내부에서 리액트 훅을 쓰는 건 규칙 위반이라 제외했습니다.
<예시>
자주 바뀌는 Context 와 자주 바뀌지 않는 Context를 따로 관리하고, 따로 구독하게 만들 수도 있습니다. 이를 통해 호출
참고:
FitLink-FE/packages/ui/src/components/DayOfWeekPicker/Context.tsx
Lines 1 to 15 in 4843435
간단하게 타입 지정 부분만 보여주면 다음과 같을 것 같네요.
다만 현재는 이렇게 해도 차이가 없을 것 같은데요. 그 이유는 1) Provider를 감싸고 있는 부분이 children 으로 받고 있는 것이 아니라 바로 자식으로 받고 있는 형태라 부모가 리렌더링되면서 자식도 모두 리렌더링되어 최적화 시도와 상관없이 자식 컴포넌트가 모두 리렌더링됨. 2) DayOfWeekPickerItem 컴포넌트에서 valueContext와 configContext를 모두 사용하기 때문에 분리해도 어차피 모두 사용하는 구조임
참고:
FitLink-FE/packages/ui/src/components/DayOfWeekPicker/index.tsx
Lines 92 to 96 in 4843435
이건 예시 코드를 짜려니 좀 많이 바꿔야할 것 같아서 작성하지 못했습니다.
다만 합성 컴포넌트는 기본적으로 "독립적으로 동작"하게 하기 위한 목적도 있는데 그 목적은 깨지게 되고 필수적으로 DayOfWeekPickerImpl에 의존하게 됩니다. 또한 props 의 참조값을 잘 관리해야 최적화가 잘 되기 때문에 신경을 잘 써야할 것 같습니다.
참고: https://github.com/pmndrs/zustand/blob/main/src/react.ts