EMZETT.
Увійти

React.cache()

Коротко: Функція React, що кешує результат асинхронного виклику (наприклад, запиту до бази даних) у межах одного запиту — якщо ви викликаєте ту саму функцію з тими самими аргументами кілька разів в одному запиті, реальна робота виконується лише раз.

Докладніше: Важливо: кеш діє лише протягом одного серверного запиту, а не між різними користувачами чи запитами (немає глобального/спільного кешу). Типовий випадок: функція на кшталт getCurrentUser() часто викликається в кількох незалежних місцях за один перегляд сторінки (наприклад, раз у шапці, раз у самій сторінці) — без React.cache() це означає кілька однакових звернень до бази даних туди й назад на один перегляд сторінки.

Наш контекст: Це була правдоподібна головна причина того, що сервер Emzett на Vercel час від часу падав, особливо за кількох одночасних відвідувачів — getCurrentUser()/getSetting() виконувалися двічі на 11 із 12 сторінок, щоразу з повним зверненням до бази даних. Після обгортання в React.cache() два виклики — перевірено наживо — перетворилися на один реальний доступ до бази даних.

Подробиці

Як це працює: React.cache() запам’ятовує для кожного запиту (технічно: для кожного проходу серверного відображення React), яка функція з якими аргументами вже викликалася, і за другого однакового виклику просто повертає вже обчислений результат замість повторного виконання функції:

import { cache } from "react";
export const getCurrentUser = cache(async () => {
  const session = await getSession();
  if (!session) return null;
  return db.query.users.findFirst({ where: eq(users.id, session.userId) });
});

Важливо відрізняти його від інших механізмів кешування Next.js, що звучать схоже, але роблять інше: кешування fetch() та теги кешу/revalidateTag кешують дані МІЖ сторінками та МІЖ запитами (доки їх явно не скинуть), тоді як React.cache() явно дедуплікує ЛИШЕ в межах життєвого циклу одного запиту, а потім відкидається — для наступного користувача чи наступного перегляду сторінки функція знову працює цілком звичайно.

Чому дедуплікація не працює для різних даних

React.cache() розпізнає «однакові виклики» за порівнянням переданих аргументів (подібно до ключа мемоїзації). Якщо викликати ту саму функцію з РІЗНИМИ аргументами, дедуплікація не застосовується — кожен унікальний набір аргументів виконується один раз на запит:

await getUserById("abc"); // справді виконується
await getUserById("abc"); // з кешу, без звернення до бази даних
await getUserById("xyz"); // інші аргументи -> знову виконується

Зазвичай це саме бажана поведінка, але часта хибна думка: той, хто вважає, що React.cache() об’єднує КОЖНЕ звернення до бази даних у запиті, здивується, коли два трохи різні виклики (наприклад, один з необов’язковим параметром, інший без) усе одно запустять два реальні запити.

Відмінність від useMemo

useMemo — клієнтська функція (виконується в браузері, на компонент, між перемальовуваннями) і призначена для синхронних обчислень — React.cache() лише серверна, створена для асинхронного доступу до даних і дедуплікує по всій ширині дерева компонентів запиту, а не лише всередині одного компонента. Вони розв’язують різні завдання й не замінюють одна одну.

Див. також: Neon, Neon Auto-Suspend, Теги кешу/revalidateTag