← Назад к вопросам

Как при помощи принципа разделения команды и запроса получить данные в двух компонентах из одного запроса?

1.0 Junior🔥 211 комментариев
#Браузер и сетевые технологии

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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>;
}

Этот подход наиболее эффективен, так как запрос выполняется один раз на сервере.

Рекомендации

  1. Для простых случаев - используй Context API + useEffect
  2. Для production проектов - SWR или React Query (TanStack Query)
  3. Для Next.js - Server Components с передачей props
  4. Для legacy кода - Custom Hook с мемоизацией

Главное правило CQRS на фронтенде: один запрос, один источник истины, распределение данных между компонентами через контекст илиProps.

Как при помощи принципа разделения команды и запроса получить данные в двух компонентах из одного запроса? | PrepBro