Синхронизация с состоянием компонента

React Router предоставляет мощные инструменты для управления навигацией и маршрутизацией в приложениях на React, но ключевым аспектом является синхронизация маршрутов с состоянием компонентов. Этот процесс позволяет динамически изменять отображение интерфейса на основе текущих параметров маршрута, состояния приложения или пользовательского ввода.


Использование хуков useLocation и useNavigate

useLocation возвращает объект, содержащий информацию о текущем URL, включая pathname, search и hash. Этот объект можно использовать для отслеживания изменений маршрута и синхронизации с локальным состоянием компонента:

import { useLocation } from 'react-router-dom';
import { useEffect, useState } from 'react';

function SearchPage() {
  const location = useLocation();
  const [query, setQuery] = useState('');

  useEffect(() => {
    const params = new URLSearchParams(location.search);
    setQuery(params.get('q') || '');
  }, [location.search]);

  return (
    <input
      type="text"
      value={query}
      onCha nge={e => setQuery(e.target.value)}
    />
  );
}

В этом примере query всегда соответствует параметру q в URL, что обеспечивает двустороннюю синхронизацию состояния и маршрута.

useNavigate позволяет программно менять маршрут и поддерживать синхронизацию состояния с URL:

import { useNavigate } from 'react-router-dom';

function SearchForm({ query }) {
  const navigate = useNavigate();

  const handleSubmit = (e) => {
    e.preventDefault();
    navigate(`/search?q=${encodeURIComponent(query)}`);
  };

  return (
    <form onSub mit={handleSubmit}>
      <input type="text" value={query} />
      <button type="submit">Поиск</button>
    </form>
  );
}

Управление состоянием с useParams

useParams позволяет получать динамические сегменты маршрута и использовать их для инициализации и синхронизации состояния компонента:

import { useParams } from 'react-router-dom';
import { useEffect, useState } from 'react';

function ProductPage() {
  const { productId } = useParams();
  const [product, setProduct] = useState(null);

  useEffect(() => {
    fetch(`/api/products/${productId}`)
      .then(res => res.json())
      .then(data => setProduct(data));
  }, [productId]);

  if (!product) return <p>Загрузка...</p>;
  return <div>{product.name}</div>;
}

Изменение маршрута автоматически вызывает обновление состояния product, благодаря чему интерфейс остается синхронизированным с URL.


С помощью Link и NavLink можно передавать состояние маршрута в объекте state, что позволяет синхронизировать маршрутизацию с внутренним состоянием компонентов без изменения URL-параметров:

import { Link, useLocation } from 'react-router-dom';

function PostList() {
  return (
    <ul>
      <li>
        <Link
          to="/post/1"
          state={{ fromList: true }}
        >
          Пост 1
        </Link>
      </li>
      <li>
        <Link
          to="/post/2"
          state={{ fromList: true }}
        >
          Пост 2
        </Link>
      </li>
    </ul>
  );
}

function PostPage() {
  const location = useLocation();
  const fromList = location.state?.fromList || false;

  return <div>{fromList ? 'Перешли со списка постов' : 'Прямой переход'}</div>;
}

Передаваемое состояние позволяет компоненту принимать решения о визуализации на основе контекста навигации, что облегчает управление сложными сценариями пользовательских переходов.


Синхронизация состояния с query-параметрами

Для сложных интерфейсов часто нужно хранить фильтры, пагинацию или сортировку в URL. В этом случае сочетание useSearchParams и useEffect позволяет создавать автоматически синхронизируемые состояния:

import { useSearchParams } from 'react-router-dom';
import { useState, useEffect } from 'react';

function ProductFilter() {
  const [searchParams, setSearchParams] = useSearchParams();
  const [category, setCategory] = useState(searchParams.get('category') || '');

  useEffect(() => {
    setCategory(searchParams.get('category') || '');
  }, [searchParams]);

  const handleChange = (e) => {
    const value = e.target.value;
    setCategory(value);
    setSearchParams({ category: value });
  };

  return (
    <sel ect value={category} onCha nge={handleChange}>
      <option value="">Все категории</option>
      <option value="electronics">Электроника</option>
      <option value="books">Книги</option>
    </select>
  );
}

Такой подход гарантирует, что URL и состояние компонента всегда совпадают, что улучшает UX и позволяет сохранять состояние между перезагрузками страницы или копированием ссылки.


Использование состояния для условного рендеринга

Состояние маршрута может использоваться для условного отображения компонентов без изменения маршрута. Это особенно полезно для модальных окон или всплывающих панелей:

import { Routes, Route, useLocation } fr om 'react-router-dom';

function App() {
  const location = useLocation();
  const background = location.state?.background;

  return (
    <>
      <Routes location={background || location}>
        <Route path="/" element={<Home />} />
        <Route path="/gallery" element={<Gallery />} />
      </Routes>

      {background && (
        <Routes>
          <Route path="/gallery/:id" element={<Modal />} />
        </Routes>
      )}
    </>
  );
}

Такое решение позволяет отделить модальные представления от основного маршрута, сохраняя синхронизацию состояния с историей браузера.


Итоговая стратегия синхронизации

  1. URL → состояние компонента: использовать useLocation, useParams, useSearchParams для отслеживания изменений маршрута.
  2. Состояние компонента → URL: использовать useNavigate или setSearchParams для обновления маршрута при изменении состояния.
  3. Контекст маршрута: передавать дополнительные данные через state в Link и NavLink.
  4. Условный рендеринг: использовать состояние маршрута для управления отображением компонентов без изменения URL.

Соблюдение этих принципов обеспечивает гибкую и надежную синхронизацию состояния компонентов с маршрутизацией, что является основой для построения сложных, динамических приложений на React.