Как при помощи принципа разделения команды и запроса получить данные в двух компонентах из одного запроса?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
CQRS принцип для получения данных в нескольких компонентах
Что такое CQRS
CQRS (Command Query Responsibility Segregation) - это архитектурный принцип, разделяющий операции изменения данных (Commands) и получения данных (Queries). На фронтенде это означает: одна функция отвечает только за запрос данных, а не за их кэширование и распределение.
В контексте вашего вопроса: нужно один раз сделать API запрос и поделить результат между двумя компонентами.
Решение 1: React Context + useEffect
Это классический подход для глобального состояния:
// DataContext.tsx
import { createContext, useContext, useEffect, useState } from 'react';
interface DataContextType {
data: any;
loading: boolean;
error: Error | null;
}
const DataContext = createContext<DataContextType | undefined>(undefined);
export function DataProvider({ children }: { children: React.ReactNode }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
// Запрос делается только один раз при монтировании
const fetchData = async () => {
try {
const response = await fetch('/api/data');
const result = await response.json();
setData(result);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
return (
<DataContext.Provider value={{ data, loading, error }}>
{children}
</DataContext.Provider>
);
}
export function useData() {
const context = useContext(DataContext);
if (!context) {
throw new Error('useData must be used within DataProvider');
}
return context;
}
Использование в компонентах:
function ComponentOne() {
const { data } = useData();
return <div>Компонент 1: {data?.title}</div>;
}
function ComponentTwo() {
const { data } = useData();
return <div>Компонент 2: {data?.description}</div>;
}
Решение 2: Custom Hook с мемоизацией
Для более простых случаев - создать хук, который кэширует запрос:
// useQuery.ts
const queryCache = new Map();
export function useQuery<T>(url: string): {
data: T | null;
loading: boolean;
error: Error | null;
} {
const [state, setState] = useState<{
data: T | null;
loading: boolean;
error: Error | null;
}>({
data: queryCache.get(url) || null,
loading: !queryCache.has(url),
error: null,
});
useEffect(() => {
// Если данные уже в кэше - не делаем запрос
if (queryCache.has(url)) {
setState(prev => ({ ...prev, loading: false }));
return;
}
const fetchData = async () => {
try {
const response = await fetch(url);
const data = await response.json();
queryCache.set(url, data);
setState({ data, loading: false, error: null });
} catch (error) {
setState({ data: null, loading: false, error: error as Error });
}
};
fetchData();
}, [url]);
return state;
}
Использование:
function ComponentOne() {
const { data } = useQuery('/api/data');
return <div>{data?.title}</div>;
}
function ComponentTwo() {
const { data } = useQuery('/api/data'); // Один и тот же URL = один запрос
return <div>{data?.description}</div>;
}
Решение 3: SWR (Stale-While-Revalidate)
Модерный подход с использованием библиотеки SWR или React Query:
import useSWR from 'swr';
const fetcher = (url: string) => fetch(url).then(r => r.json());
function ComponentOne() {
const { data } = useSWR('/api/data', fetcher);
return <div>{data?.title}</div>;
}
function ComponentTwo() {
const { data } = useSWR('/api/data', fetcher); // Автоматически дедублицируется
return <div>{data?.description}</div>;
}
SWR автоматически:
- Кэширует результаты
- Дедублицирует запросы (если несколько компонентов одновременно запрашивают один URL)
- ПереValidирует данные в фоне
- Обрабатывает ошибки
Решение 4: Server Component + Props (Next.js)
В Next.js можно использовать Server Components для получения данных один раз на сервере:
// app/page.tsx (Server Component)
async function getData() {
const response = await fetch('https://api.example.com/data');
return response.json();
}
export default async function Page() {
const data = await getData(); // Запрос на сервере
return (
<>
<ComponentOne data={data} />
<ComponentTwo data={data} />
</>
);
}
function ComponentOne({ data }: { data: any }) {
return <div>{data.title}</div>;
}
function ComponentTwo({ data }: { data: any }) {
return <div>{data.description}</div>;
}
Этот подход наиболее эффективен, так как запрос выполняется один раз на сервере.
Рекомендации
- Для простых случаев - используй Context API + useEffect
- Для production проектов - SWR или React Query (TanStack Query)
- Для Next.js - Server Components с передачей props
- Для legacy кода - Custom Hook с мемоизацией
Главное правило CQRS на фронтенде: один запрос, один источник истины, распределение данных между компонентами через контекст илиProps.