메모이제이션(수동 또는 자동)과 호환되지 않는 라이브러리 사용에 대해 검증합니다.
규칙 세부 사항
일부 라이브러리는 React에서 지원하지 않는 패턴을 사용합니다. 린터가 알려진 목록에서 이러한 API의 사용을 감지하면 이 규칙에 따라 플래그를 지정합니다. 이는 React 컴파일러가 앱을 손상시키지 않기 위해 이러한 호환되지 않는 API를 사용하는 컴포넌트를 자동으로 건너뛸 수 있음을 의미합니다.
// 이러한 라이브러리로 메모이제이션이 깨지는 예시
function Form() {
const { watch } = useForm();
// ❌ 'name' 필드가 변경되어도 이 값은 절대 업데이트되지 않습니다
const name = useMemo(() => watch('name'), [watch]);
return <div>Name: {name}</div>; // UI가 "얼어붙은" 것처럼 보입니다
}React 컴파일러는 React 규칙을 따라 값을 자동으로 메모이제이션합니다. 수동 useMemo로 문제가 발생하면 컴파일러의 자동 최적화도 깨집니다. 이 규칙은 이러한 문제가 있는 패턴을 식별하는 데 도움이 됩니다.
자세히 살펴보기
라이브러리 API나 Hook을 설계할 때 고려해야 할 질문 중 하나는 API 호출을 useMemo로 안전하게 메모이제이션할 수 있는지 여부입니다. 그렇지 않다면 수동 메모이제이션과 React 컴파일러 메모이제이션 모두 사용자의 코드를 손상시킬 것입니다.
예를 들어, 이러한 호환되지 않는 패턴 중 하나는 “내부 가변성”입니다. 내부 가변성은 객체나 함수가 참조는 동일하게 유지되지만 시간이 지남에 따라 변경되는 자체 숨겨진 상태를 유지하는 것을 말합니다. 외부에서는 동일해 보이지만 내용물을 은밀하게 재배치하는 상자라고 생각하면 됩니다. React는 다른 상자를 받았는지만 확인하고 안에 무엇이 들어 있는지는 확인하지 않기 때문에 변경 사항을 알 수 없습니다. 이는 메모이제이션을 깨뜨리는데, React는 값의 일부가 변경된 경우 외부 객체(또는 함수)가 변경되는 것에 의존하기 때문입니다.
React API를 설계할 때의 경험 법칙으로, useMemo가 이를 깨뜨릴지 생각해보세요.
function Component() {
const { someFunction } = useLibrary();
// 이와 같은 함수를 메모이제이션하는 것은 항상 안전해야 합니다
const result = useMemo(() => someFunction(), [someFunction]);
}대신, 불변 상태를 반환하고 명시적인 업데이트 함수를 사용하는 API를 설계하세요.
// ✅ 좋은 예시: 업데이트될 때 참조가 변경되는 불변 상태를 반환
function Component() {
const { field, updateField } = useLibrary();
// 이것은 항상 메모이제이션하기에 안전합니다
const greeting = useMemo(() => `Hello, ${field.name}!`, [field.name]);
return (
<div>
<input
value={field.name}
onChange={(e) => updateField('name', e.target.value)}
/>
<p>{greeting}</p>
</div>
);
}잘못된 예시
이 규칙에 대한 잘못된 코드 예시입니다.
// ❌ react-hook-form `watch`
function Component() {
const {watch} = useForm();
const value = watch('field'); // 내부 가변성
return <div>{value}</div>;
}
// ❌ TanStack Table `useReactTable`
function Component({data}) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
});
// table 인스턴스가 내부 가변성을 사용합니다
return <Table table={table} />;
}올바른 예시
이 규칙에 대한 올바른 코드 예시입니다.
// ✅ react-hook-form의 경우 `useWatch`를 사용하세요
function Component() {
const {register, control} = useForm();
const watchedValue = useWatch({
control,
name: 'field'
});
return (
<>
<input {...register('field')} />
<div>현재 값: {watchedValue}</div>
</>
);
}일부 다른 라이브러리는 아직 React의 메모이제이션 모델과 호환되는 대체 API가 없습니다. 린터가 이러한 API를 호출하는 컴포넌트나 Hook을 자동으로 건너뛰지 않는다면 이슈를 제출하여 린터에 추가할 수 있도록 해주세요.