React Auth, завантаження даних та помилки: Axios, React Router, Redux, TanStack Query
Десять помилок у React щодо автентифікації, завантаження даних та помилок, виправлених за допомогою інтерцепторів Axios, лоадерів React Router, мідлвейру Redux, TanStack Query та меж помилок.
Джерела: Stop Polluting Your Components: The React ‘Middleware’ Guide (Axios, Routers, and State)
На бекенді ніхто не перевіряє автентифікацію в кожному обробнику маршрутів чи не додає функції логування до кожного контролера; ця робота виконується через мідлвейр, який встановлюється один раз та застосовується до кожного запиту. Кодові бази React часто втрачають цю дисципліну. Компоненти починають самостійно обробляти вставку токенів, перенаправлення 401, логування помилок та отримання даних, що призводить до зайвого коду, непослідовної поведінки та помилок, які з’являються лише тоді, коли користувач натискає елементи певною послідовністю. У React-додатку є власний життєвий цикл запиту, де API-запити, переходи між маршрутами та оновлення стану проходять через шари, до яких можна під’єднатися. Цей посібник розглядає десять поширених помилок у інтерцепторах Axios, лоадерах React Router, бібліотеках стану, межах помилок та мідлвейрі Next.js та показує, де саме має знаходитися кожен елемент цієї системи.
Проблеми транспортування: автентифікація та інтерцептори Axios
Помилка 1: обробка відповідей 401 у кожному компоненті
Обробка автентифікації є найпоширенішою причиною зайвого коду в компонентах. Кожна функція fetch має власний блок catch, який перевіряє наявність коду 401, очищує зберігання даних та перенаправляє користувача:
// ❌ Bad: Repeated in every component
async function fetchDashboard() {
try {
const res = await axios.get('/api/dashboard');
return res.data;
} catch (error) {
if (error.response?.status === 401) {
localStorage.removeItem('token');
window.location.href = '/login';
}
throw error;
}
}
async function fetchProfile() {
try {
const res = await axios.get('/api/profile');
return res.data;
} catch (error) {
if (error.response?.status === 401) {
localStorage.removeItem('token');
window.location.href = '/login';
}
throw error;
}
}
Окрім повторень, цей підхід є крихким. Перехід від локального зберігання до куки означає необхідність редагування кожного місця використання цих даних. Додавання механізму оновлення токенів перед перенаправленням знову вимагає змін у кожному файлі. А одна-єдина забута перевірка залишає додаток у стані напіввиходу з облікового запису.
Рішенням є використання інтерцептора відповідей Axios на спільній інстанції клієнта, який отримує кожну відповідь раніше, ніж це роблять компоненти. У наведеній нижче версії токен також додається під час виходу з додатку, а при помилці 401 відбувається одна спроба оновлення (захищена флагом _retry, щоб уникнути циклу), після чого відправляється початковий запит із новим токеном; лише у разі невдачі оновлення застосовується варіант очищення зберігання та перенаправлення.
// api/client.js
import axios from 'axios';
const api = axios.create({
baseURL: process.env.REACT_APP_API_URL,
});
// Request interceptor: attach token
api.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Response interceptor: handle global errors
api.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
// Attempt token refresh
const refreshToken = localStorage.getItem('refreshToken');
const { data } = await axios.post('/auth/refresh', { refreshToken });
localStorage.setItem('token', data.accessToken);
originalRequest.headers.Authorization = `Bearer ${data.accessToken}`;
return api(originalRequest);
} catch (refreshError) {
// Refresh failed, redirect to login
localStorage.removeItem('token');
localStorage.removeItem('refreshToken');
window.location.href = '/login';
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);
export default api;
Компоненти тепер просто імпортують api та викликають його, не усвідомлюючи механізм обробки помилки 401; тепер єдиним місцем, де вирішується, як відновити автентифікацію, є інтерцептор. Існують три застереження. process.env.REACT_APP_API_URL — це стандарт Create React App; у проекті Vite слід використовувати import.meta.env.VITE_API_URL. Якщо кілька запитів зазнають невдачі одночасно, кожен з них ініціює власне оновлення, що може зробити недійсними токени оновлення; рішення цієї проблеми описано у чому одночасні помилки 401 виводять користувачів. Крім того, зберігання токенів у localStorage робить їх вразливими до будь-яких помилок типу XSS, тому варто розглянути використання httpOnly-кукі.
Помилка 2: відсутність повернення значення з інтерцептора
Інтерцептори утворюють ланцюг, і кожен елемент мусить передати своє значення наступному. Ці двоє забувають:
// ❌ Bad: Broken chain
api.interceptors.response.use((response) => {
console.log('Response received:', response.status);
// Forgot to return response!
});
api.interceptors.request.use((config) => {
config.headers['X-Request-ID'] = crypto.randomUUID();
// Forgot to return config!
});
Цей ефект часто описують як «зависання запиту», але у поточних версіях Axios помилка зазвичай є більш прямою та такою ж заплутаною. Інтерцептор відповідей, який нічого не повертає, вирішує обіцянку викликаючого коду значенням undefined, тож const { data } = await api.get('/user') викликає помилку деконструкції, далеку від її справжньої причини. Інтерцептор запитів, який нічого не повертає, залишає Axios без конфігурації, і запит зазнає невдачі ще до того, як буде надісланий. У будь-якому разі баг проявляється не там, де його було створено.
Завжди повертайте те, що отримали, та чітко відхиляйте запит у обробниках помилок, щоб блок catch викликаючого коду все одно виконувався:
// ✅ Good: Complete chain
api.interceptors.request.use(
(config) => {
config.headers['X-Request-ID'] = crypto.randomUUID();
return config; // Critical
},
(error) => Promise.reject(error)
);
api.interceptors.response.use(
(response) => {
// Log without breaking the chain
console.log(`[${response.config.url}] ${response.status}`);
return response; // Critical
},
(error) => {
// Always reject so the component catch block fires
return Promise.reject(error);
}
);
Аналогія з Express залишається актуальною: ігнорування next() зупиняє обробку запиту в Express, а ігнорування return руйнує ланцюг перехоплювачів.
Помилка 3: ручне додавання заголовків автентифікації
Копіювання заголовка Authorization у кожен виклик розглядає проблему, яка стосується кількох частин коду, як локальну. Рано чи пізно якийсь виклик пропустить його, сформатує його інакше або надішле Bearer null. Наведений нижче фрагмент також демонструє, наскільки легко припуститися помилки: у виклику post об’єкт заголовка передається як тіло запиту, а дані — як конфігурація, оскільки Axios очікує формат post(url, data, config):
// ❌ Bad: Manual headers everywhere
api.get('/user', { headers: { Authorization: `Bearer ${token}` } });
api.post('/order', { headers: { Authorization: `Bearer ${token}` } }, payload);
api.get('/history', { headers: { Authorization: `Bearer ${token}` } });
Інтерцептор запитів усуває всю категорію помилок шляхом додавання заголовка в одному місці, лише за наявності токена:
// ✅ Good: Global token injection
api.interceptors.request.use((config) => {
const token = localStorage.getItem('access_token');
if (token && config.headers) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
Якщо ви використовуєте TanStack Query, його кеші також пропонують глобальні хуки для обробки побічних ефектів. Функція QueryCache onError може перенаправляти при коді 401 для будь-якого запиту, а функція MutationCache onError може відображати сповіщення для будь-якої невдалої мутації:
// ✅ Good: QueryClient global handlers
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: (error) => {
if (error.response?.status === 401) {
window.location.href = '/login';
}
}
}),
mutationCache: new MutationCache({
onError: (error) => {
toast.error(error.message);
}
})
});
Основний принцип полягає у тому, що автентифікація є питанням транспортування, а не компонента, тому її слід обробляти на рівні шару транспортування.
Завантаження даних: загрузчики React Router та кеш TanStack Query
Помилка 4: отримання даних у useEffect замість використання загрузчика маршруту
Звичний підхід полягає у налаштуванні компонента, виконанні ефекту, відображенні індикатора завантаження, отриманні даних та їх відображенні:
// ❌ Bad: Component-level fetching
function Dashboard() {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
api.get('/api/dashboard')
.then(res => setData(res.data))
.finally(() => setIsLoading(false));
}, []);
if (isLoading) return <Spinner />;
return <DashboardContent data={data} />;
}
Це створює «водоспади запитів»: роутер відображає компонент, лише після цього ефект починає надсилати запит, а лише після його повернення оновлюється стан. Переходи туди-назад щоразу змушують перезавантажувати дані. У старіших версіях React також були попередження щодо зміни стану після демонтажу компонента; у React 18 це попередження було видалено, але проблема конкуренції між повільною та швидкою відповіддю залишається актуальною.
API для роботи з даними у React Router (доступні за допомогою createBrowserRouter з версії v6.4) дозволяють маршруту вказати loader, який завантажує дані перед відображенням елемента маршруту, та errorElement, який відображається у разі помилки лоадера:
// ✅ Good: Route-level data loading
// router.jsx
import { createBrowserRouter } from 'react-router-dom';
import { Dashboard, dashboardLoader } from './Dashboard';
const router = createBrowserRouter([
{
path: '/dashboard',
element: <Dashboard />,
loader: dashboardLoader, // Runs before render
errorElement: <DashboardError />
}
]);
// Dashboard.jsx
import { useLoaderData } from 'react-router-dom';
export async function dashboardLoader() {
const response = await api.get('/api/dashboard');
if (response.status !== 200) {
throw new Response('Failed to load dashboard', { status: response.status });
}
return response.data;
}
export function Dashboard() {
const data = useLoaderData(); // Already available, no loading state needed
return <DashboardContent data={data} />;
}
Коли відображається Dashboard, useLoaderData() вже має дані, тому немає необхідності у стані завантаження на рівні компонента чи додатковому коді для його обробки. Варто знати два важливі моменти. Сама навігація чекає на завантажувач, тому все одно потрібен глобальний індикатор очікування, керований useNavigation. Крім того, оскільки Axios за замовчуванням відхиляє відповіді, що не є 2xx, перевірка response.status !== 200 у цьому завантажувачі ніколи не фіксує статус помилки; відхилення самостійно передається до errorElement.
Для додаткових даних, які не повинні блокувати сторінку, завантажувач може дочекатися критичного запиту та передати повільний запит як нерозв’язану обіцянку за допомогою defer:
export async function dashboardLoader() {
const criticalData = await api.get('/api/summary');
const slowData = api.get('/api/analytics'); // Promise, not awaited
return defer({
summary: criticalData.data,
analytics: slowData.then(r => r.data)
});
}
Сторінка відображається з резюме, поки надходять дані аналітики, зазвичай за допомогою Await та Suspense. У React Router v7 параметр defer більше не є обов’язковим, і лоадери можуть безпосередньо повертати обіцянки, тому перевірте документацію до версії, яку ви використовуєте.
Помилка 5: ручне написання thunks для кожного запиту
Redux Thunk став великим кроком вперед, коли з’явився, але написання окремого thunk для кожного API-запиту сьогодні означає необхідність підтримки констант типів дій, функцій створення дій, варіантів редюсера та прапорців статусу для завдань, які сучасні інструменти виконують декларативно:
// ❌ Bad: Thunk boilerplate for simple fetching
const FETCH_USER_REQUEST = 'FETCH_USER_REQUEST';
const FETCH_USER_SUCCESS = 'FETCH_USER_SUCCESS';
const FETCH_USER_FAILURE = 'FETCH_USER_FAILURE';
const fetchUser = (userId) => async (dispatch) => {
dispatch({ type: FETCH_USER_REQUEST });
try {
const res = await api.get(`/users/${userId}`);
dispatch({ type: FETCH_USER_SUCCESS, payload: res.data });
} catch (error) {
dispatch({ type: FETCH_USER_FAILURE, payload: error.message });
}
};
// Reducer boilerplate...
// Component mapStateToProps...
Ця структура була доцільною, коли не існувало нічого кращого. Для стандартних операцій CRUD вона тепер зайва.
RTK Query та TanStack Query заздалегідь надають можливості кешування, усунення дублікатів запитів, фонового оновлення даних та керування статусами завантаження. За допомогою RTK Query ви описуєте базовий запит, включаючи хук prepareHeaders, який додає токен, та вказуєте кінцеві точки; для вас генеруються хуки на кшталт useGetUserQuery та useUpdateUserMutation:
// ✅ Good: RTK Query handles the middleware
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const userApi = createApi({
reducerPath: 'userApi',
baseQuery: fetchBaseQuery({
baseUrl: '/api',
prepareHeaders: (headers) => {
const token = localStorage.getItem('token');
if (token) headers.set('authorization', `Bearer ${token}`);
return headers;
}
}),
endpoints: (builder) => ({
getUser: builder.query({
query: (id) => `/users/${id}`,
}),
updateUser: builder.mutation({
query: ({ id, ...body }) => ({
url: `/users/${id}`,
method: 'PUT',
body,
}),
}),
}),
});
export const { useGetUserQuery, useUpdateUserMutation } = userApi;
За цими хуками RTK Query зберігає у кеші відповіді для кожного кінцевої точки та аргументу, підраховує, скільки компонентів використовують кожен запис, та видаляє записи, якими більше ніхто не користується. Зауважте, що він не нормалізує ентитети так, як це робить вручну створений адаптер ентитетів; результати, збережені в кеші, залишаються у тому вигляді, у якому були повернуті. У будь-якому разі ви пишете бізнес-логіку, а бібліотека опікується внутрішніми процесами.
Мідлвейр Redux та механізми безпеки UI
Помилка 6: зміна стану всередині мідлвейру Redux
Самостійний мідлвейр Redux є гарним місцем для логування, аналітики та трансформації дій. Це неправильне місце для зміни стану. Мідлвейр виконується між викликом dispatch та редюсером, тож зміни стану там обходять систему відстеження змін Redux, ускладнюють дебагування та заплутують інструменти DevTools:
// ❌ Bad: State mutation in middleware
const analyticsMiddleware = store => next => action => {
if (action.type === 'ADD_TO_CART') {
// Directly mutating state! Never do this.
store.getState().cart.lastAddedAt = Date.now();
}
return next(action);
};
Ця мутація також не змушує переробляти вміст, оскільки Redux та його інтерфейси виявляють зміни шляхом порівняння посилань. Зміна поля у вже існуючому об’єкті залишає посилання незмінним, тому підписники не бачать жодних оновлень. Перевірка незмінності у режимі розробки Redux Toolkit виявить такий тип мутації, що є ще однією причиною для його використання.
Обмежте проміжні компоненти можливостями читання, логування, трансформації чи передачі дій, а всі зміни стану зберігайте у редюсерах. Тут проміжні компоненти відстежують дію та додають часовий позначник у вигляді meta, а редюсер виконує оновлення з дотриманням принципу незмінності, використовуючи цей позначник:
// ✅ Good: Middleware only intercepts and forwards
const analyticsMiddleware = store => next => action => {
// Log the action before it reaches the reducer
analytics.track(action.type, {
payload: action.payload,
timestamp: Date.now(),
userId: store.getState().auth.userId,
});
// Optionally transform the action
if (action.type === 'ADD_TO_CART') {
action = {
...action,
meta: { addedAt: Date.now() }
};
}
return next(action); // Forward to reducer
};
// Reducer handles the state update
function cartReducer(state = initialState, action) {
switch (action.type) {
case 'ADD_TO_CART':
return {
...state,
items: [...state.items, action.payload],
lastAddedAt: action.meta.addedAt, // Proper immutable update
};
default:
return state;
}
}
Зверніть увагу на взаємозв’язок: редьюсер читає action.meta.addedAt, тому він залежить від встановленого мідлвейру. Створення часової позначки у функції створення дії (або у калебуці prepare у Redux Toolkit) усуває цю приховану залежність. Корисне уявлення: мідлвейр — це поштове відділення, яке може читати конверти та додавати марки, але ніколи не відкриває ваш рахунок у банку, щоб змінити сальдо.
Помилка 7: сприйняття меж помилок як чогось другорядного
Багато додатків мають одну межу навколо кореня або ж жодної. Межі помилок функціонують як мідлвейр для дерева компонентів: вони ловлять помилки відображення у своїх дочірніх елементах та запобігають тому, щоб одна несправність зруйнувала всю інтерфейсну структуру. Без них некоректна відповідь API, відсутня властивість чи несправний скрипт від стороннього постачальника можуть зробити всю сторінку порожньою:
// ❌ Bad: No error boundaries
function App() {
return (
<div>
<Header />
<Sidebar />
<Dashboard /> {/* If this crashes, everything dies */}
<Footer />
</div>
);
}
Розмістіть межі на рівні макету та елементів, щоб збій у віджеті аналітики не призводив до втрати навігації. Межа — це компонент класу, який встановлює стан помилки у функції getDerivedStateFromError, повідомляє про це у функції componentDidCatch та відображає альтернативний варіант; використання кількох меж дозволяє кожній області відновлюватися самостійно:
// ✅ Good: Strategic boundary placement
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
return this.props.fallback || <DefaultErrorUI error={this.state.error} />;
}
return this.props.children;
}
}
function App() {
return (
<ErrorBoundary fallback={<GlobalErrorPage />}>
<Header />
<div className="layout">
<ErrorBoundary fallback={<SidebarError />}>
<Sidebar />
</ErrorBoundary>
<main>
<ErrorBoundary fallback={<WidgetError />}>
<AnalyticsWidget />
</ErrorBoundary>
<ErrorBoundary fallback={<WidgetError />}>
<RecentActivity />
</ErrorBoundary>
</main>
</div>
</ErrorBoundary>
);
}
Пам’ятайте про їхні обмеження: ці межі ловлять помилки, що виникають під час відображення, у методах життєвого циклу та конструкторах, розташованих нижче, але не помилки в обробниках подій, у асинхронному коді, такому як таймери чи обіцянки без використання оператора await, або в самій межі. Для цього потрібні звичайні конструкції try/catch чи глобальні обробники. Якщо ви використовуєте фреймворк із серверними компонентами, поєднуйте клієнтські межі з його файлами обробки помилок на рівні маршрутів (наприклад, error.js у Next.js App Router), щоб ловити серверні помилки для кожного маршруту та клієнтські помилки для кожного компонента.
Продуктивність та межі платформи
Помилка 8: важкі операції всередині інтерцепторів
Інтерцептори є асинхронними з точки зору обіцянок, але їхній код все одно виконується на основній смузі. Витратні операції з декриптуванням, трансформацією чи парсингом там блокують процес відображення:
// ❌ Bad: Heavy work in interceptor
api.interceptors.response.use((response) => {
// This blocks rendering for large payloads
response.data = heavyDecryptAndTransform(response.data);
return response;
});
Трансформація, що триває 200 мілісекунд, — це період у 200 мілісекунд, протягом якого сторінка ігнорує вхідні дані, що на телефоні створює враження замороженого додатку. Перенесіть важкі обчислення у Web Worker або відкладіть все неважливе. Тут відповіді експортуються до робітника, а інтерцептор чекає на результат:
// ✅ Good: Async offloading for heavy work
api.interceptors.response.use(async (response) => {
if (response.config.url.includes('/export')) {
// Move heavy work off main thread
const worker = new Worker(new URL('./transform.worker.js', import.meta.url));
const transformed = await new Promise((resolve, reject) => {
worker.onmessage = (e) => resolve(e.data);
worker.onerror = reject;
worker.postMessage(response.data);
});
worker.terminate();
response.data = transformed;
}
return response;
});
Створення нового працівника за кожною відповіддю має початкові витрати, а postMessage копіює дані за допомогою структурованого клонування, тому для частих викликів краще використовувати працівника з довгим терміном існування та розглянути можливість використання передаваних буферів. Для шифрування чи стиснення використання Web Crypto API всередині працівника або переміщення обробки на сервер допомагає зберегти відгук UI. Для отримання додаткової інформації про цей компроміс дивіться коли варто переміщувати обробку CPU у Web Worker.
Помилка 9: сприйняття мідлвейру Next.js як коду додатку
Мідлвейр Next.js традиційно працює на середовищі Edge Runtime — це ізольована версія V8, а не повний Node.js. Він не може отримати доступ до контексту React чи стану клієнта, і там немає доступу до звичайного пулу з’єднань до бази даних. Проте часто можна побачити складну валідацію, запити до бази даних чи логіку, яка залежить від стану, у файлі middleware.ts:
// ❌ Bad: Trying to run React logic in Edge Middleware
// middleware.ts
import { NextResponse } from 'next/server';
export async function middleware(request) {
// This won't work: no access to React context
const user = await db.user.findUnique(...); // No DB connection in Edge!
// This is risky: heavy computation in Edge
const result = complexAlgorithm(request.body);
return NextResponse.next();
}
Обмежуйтеся легкими операціями на рівні запитів: переписуванням, простими перевірками наявності куки, розподілом на групи типу A/B, фільтрацією ботів та маршрутизацією за геолокацією. У цьому прикладі непідтверджені відвідувачі перенаправляються подалі від /dashboard, заголовок експерименту надсилається до додатку, а matcher використовується лише для обробки тих шляхів, де це необхідно:
// ✅ Good: Lightweight edge middleware
import { NextResponse } from 'next/server';
export function middleware(request) {
const token = request.cookies.get('token')?.value;
// Simple auth check: presence only
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
// A/B testing: lightweight header injection
const experiment = request.cookies.get('exp')?.value || 'control';
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-experiment', experiment);
return NextResponse.next({
request: { headers: requestHeaders }
});
}
export const config = {
matcher: ['/dashboard/:path*', '/api/protected/:path*']
};
Справжня перевірка сеансу має відбуватися у обробниках маршрутів або серверних компонентах із повним доступом до сервера; мідлвейр — це лише вхідні двері, а не центр керування. Підтримка під час виконання постійно змінюється: останні версії Next.js додали опцію виконання на Node.js для мідлвейру та змінили правила найменування файлів, тому перевірте поточну поведінку у документації Next.js для вашої версії.
Помилка 10: надмірна складність для невеликого додатку
Не кожному проекту потрібен набір інтерцепторів, спеціальних мідлвейрів Redux та працівників. Легко знайти тристорінкові сайти портфоліо з складними ланцюгами інтерцепторів, обмеженнями навколо статичного тексту та повним шаром запитів лише для однієї форми зв’язку:
// ❌ Bad: Over-engineered simple app
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: Infinity,
cacheTime: Infinity,
retry: 3,
retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
refetchOnWindowFocus: true,
refetchOnReconnect: true,
// ... 20 more options for a static site
}
}
});
Це культ карго, а не архітектура: кожен шар додає додаткове навантаження на обробку даних, збільшує вагу пакетів та спричиняє непрямість під час дебаггінгу. Додаток із кількома маршрутами та одним запитом не потребує стратегії кешування. Конфігурація також є внутрішньо суперечливою: за налаштуванням staleTime: Infinity дані ніколи не вважаються застарілими, тому прапорці focus та reconnect refetch не виконують жодних дій. Крім того, у TanStack Query v5 параметр cacheTime було перейменовано на gcTime.
Масштабуйте інфраструктуру разом із додатком:
- Статичні або прості сайти: використовуйте
fetchбезпосередньо, без інтерцепторів. - Невеликі додатки із автентифікацією: один інтерцептор запитів для обробки токенів та один інтерцептор відповідей для ситуацій 401.
- Середні додатки із маршрутизацією: додайте завантажувачі маршрутизатора та межі помилок на рівні лейауту.
- Великі додатки із складним станом: використовуйте RTK Query або TanStack Query та власний мідлвейр.
Знати, коли не варто додавати абстракцію, — це частина роботи.
Підхід до мідлвейру для додатків React
Мідлвейр — це не лише ідея для бекенду. У React це шар, який дозволяє компонентам зосереджуватися на відображенні, тоді як спільні завдання обробляються один раз.
- Розміщуйте автентифікацію та переклад помилок у шарі транспортування, на одній інстанції клієнта, та завжди повертайте результати або відхиляйте їх через інтерцептори.
- Завантажуйте дані маршрутів у лоадери чи кеш запитів замість використання ефектів компонентів.
- Зберігайте лише побічні ефекти мідлвейру Redux та дозвольте редукторам керувати змінами стану.
- Використовуйте кілька меж помилок та пам’ятайте, що вони не можуть перехопити.
- Уникайте виконання важких операцій на основній смузі та зберігайте мідлвейр на краях легким.
- Додавайте кожен шар лише тоді, коли розмір додатку це дозволяє.