НейроАгент

Программная навигация в React Router: Полное руководство

Узнайте, как реализовать программную навигацию в React Router v6 с помощью хука useNavigate. Навигация из выпадающих списков, форм и других компонентов без элементов Link. Полное руководство с примерами кода.

Вопрос

Как я могу программно перемещаться с помощью React Router?

С React Router я могу использовать элемент Link для создания ссылок, которые обрабатываются нативно React Router. Я вижу, что внутренне он вызывает this.context.transitionTo(...).

Я хочу выполнять перемещение. Не из ссылки, а из выпадающего списка (в качестве примера). Как я могу сделать это в коде? Что такое this.context?

Я видел миксин Navigation, но могу ли я сделать это без миксинов?

НейроАгент

Программная навигация в React Router v6 достигается с помощью хука useNavigate для функциональных компонентов или хука useHistory в более старых версиях. Вы можете вызывать возвращаемую функцию с указанием пути назначения или объекта location для запуска навигации из любого компонента, включая выпадающие списки или другие интерактивные элементы.

Содержание

Начало работы с программной навигацией

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

В современном React Router v6 основным подходом является использование хука useNavigate, который предоставляет функцию, которую можно вызвать либо с указанием строкового пути, либо с объектом location для запуска навигации. Этот подход заменяет более старый компонент высшего порядка withRouter и миксин Navigation, упомянутые в вашем вопросе.

this.context, о котором вы упоминали, относится к API контекста React, который ранее использовался React Router для предоставления информации о маршрутизации компонентам. Однако в современных React-приложениях хуки стали предпочтительным подходом для доступа к контексту и другим возможностям React.

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

Хук useNavigate - это стандартный способ обработки программной навигации в React Router v6. Вот как его реализовать:

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

function MyComponent() {
  const navigate = useNavigate();
  
  const handleDropdownChange = (event) => {
    const selectedValue = event.target.value;
    navigate(selectedValue);
  };
  
  return (
    <select onChange={handleDropdownChange}>
      <option value="/home">Главная</option>
      <option value="/about">О нас</option>
      <option value="/contact">Контакты</option>
    </select>
  );
}

Навигация с параметрами

Вы также можете передавать параметры в функцию навигации для получения большего контроля:

jsx
const navigate = useNavigate();

// Навигация с опцией replace (не добавляется в историю)
navigate('/dashboard', { replace: true });

// Навигация с состоянием
navigate('/profile', { 
  state: { from: '/dashboard' },
  replace: false
});

Относительная навигация

Для относительной навигации можно использовать опцию relative:

jsx
// Навигация на один уровень вверх
navigate('..', { relative: 'path' });

// Навигация в пределах того же маршрута, но с другими параметрами
navigate('..', { relative: 'path' });

Навигация из классовых компонентов

Хотя функциональные компоненты с хуками являются современным подходом, вам все еще может потребоваться обрабатывать навигацию в классовых компонентах. Вот несколько методов:

Использование контекста React Router

Вы можете напрямую получить доступ к контексту роутера в классовых компонентах:

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

class MyComponent extends React.Component {
  static contextType = RouterContext;
  
  handleNavigation = () => {
    this.context.navigate('/new-path');
  };
  
  render() {
    return <button onClick={this.handleNavigation}>Навигация</button>;
  }
}

Шаблон компонента высшего порядка

Создайте компонент высшего порядка для внедрения навигации:

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

function withRouter(Component) {
  function ComponentWithRouterProp(props) {
    let navigate = useNavigate();
    return <Component {...props} navigate={navigate} />;
  }
  return ComponentWithRouterProp;
}

// Использование в классовом компоненте
class MyComponent extends React.Component {
  handleClick = () => {
    this.props.navigate('/target');
  };
  
  render() {
    return <button onClick={this.handleClick}>Навигация</button>;
  }
}

export default withRouter(MyComponent);

Расширенные параметры навигации

Навигация с объектами location

Вместо простых строк вы можете передавать полные объекты location:

jsx
const location = {
  pathname: '/users',
  search: '?sort=asc',
  hash: '#section1',
  state: { from: '/dashboard' }
};

navigate(location);

Программная навигация с подтверждением

Вы можете добавлять диалоговые окна подтверждения перед навигацией:

jsx
const handleNavigate = () => {
  if (window.confirm('Вы уверены, что хотите покинуть страницу?')) {
    navigate('/new-page');
  }
};

Навигация на основе условий

jsx
const navigate = useNavigate();

const handleSubmit = async () => {
  const data = await submitForm();
  if (data.success) {
    navigate('/success');
  } else {
    navigate('/error');
  }
};

При программной навигации часто требуется передать состояние в целевой компонент:

jsx
// Исходный компонент
const navigate = useNavigate();
const handleClick = () => {
  navigate('/profile', {
    state: { 
      userId: '123',
      timestamp: Date.now(),
      referrer: '/dashboard'
    }
  });
};

// Целевой компонент
import { useLocation } from 'react-router-dom';

function ProfilePage() {
  const location = useLocation();
  const { state } = location;
  
  // Доступ к переданному состоянию
  const userId = state?.userId;
  const referrer = state?.referrer;
  
  return <div>Профиль пользователя {userId}</div>;
}

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

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

Миграция с v5 на v6

Если вы мигрируете с React Router v5, вот ключевые изменения:

Руководство по миграции с v5 на v6

Хук истории в v5:

jsx
// React Router v5
import { useHistory } from 'react-router-dom';

function Component() {
  const history = useHistory();
  history.push('/new-path');
}

Хук Navigate в v6:

jsx
// React Router v6
import { useNavigate } from 'react-router-dom';

function Component() {
  const navigate = useNavigate();
  navigate('/new-path');
}

Ключевые отличия

  1. Имена хуков: useHistoryuseNavigate
  2. Имена методов: history.push()navigate()
  3. Параметры: history.push(path, state)navigate(path, { state })
  4. Относительная навигация: Новая опция relative в v6

Типовые шаблоны миграции

Замена withRouter:

jsx
// v5 - withRouter HOC
export default withRouter(MyComponent);

// v6 - Пользовательский HOC
import { useNavigate } from 'react-router-dom';

function withNavigation(Component) {
  return function WrappedComponent(props) {
    const navigate = useNavigate();
    return <Component {...props} navigate={navigate} />;
  };
}

Лучшие практики

1. Предпочитайте хуки вместо контекста

Современный React Router рекомендует использовать хуки вместо старого подхода с контекстом:

jsx
// Рекомендуется: использование хуков
const navigate = useNavigate();

// Не рекомендуется: прямой доступ к контексту
const context = useContext(RouterContext);

2. Обрабатывайте состояния загрузки при навигации

Предоставляйте обратную связь во время навигации:

jsx
function SubmitForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const navigate = useNavigate();
  
  const handleSubmit = async () => {
    setIsSubmitting(true);
    try {
      await submitData();
      navigate('/success');
    } catch (error) {
      navigate('/error');
    } finally {
      setIsSubmitting(false);
    }
  };
  
  return (
    <button 
      onClick={handleSubmit} 
      disabled={isSubmitting}
    >
      {isSubmitting ? 'Отправка...' : 'Отправить'}
    </button>
  );
}

3. Используйте TypeScript для проверки типов

При использовании TypeScript вы можете получить лучшую безопасность типов:

typescript
import { useNavigate, NavigateFunction } from 'react-router-dom';

function Component() {
  const navigate: NavigateFunction = useNavigate();
  
  // TypeScript поможет отловить неверные пути
  navigate('/valid-path'); // ✅
  navigate('/invalid-path'); // ❌ Покажет ошибку типа
}

4. Избегайте жестко заданных путей

Используйте константы или конфигурацию для путей навигации:

jsx
const ROUTES = {
  HOME: '/',
  ABOUT: '/about',
  DASHBOARD: '/dashboard',
  PROFILE: '/profile',
};

function Component() {
  const navigate = useNavigate();
  
  const navigateToDashboard = () => {
    navigate(ROUTES.DASHBOARD);
  };
}

5. Обрабатывайте ошибки навигации

Реализуйте обработку ошибок для навигации:

jsx
import { useNavigate } from 'react-router-dom';
import { toast } from 'react-toastify';

function Component() {
  const navigate = useNavigate();
  
  const safeNavigate = (path: string) => {
    try {
      navigate(path);
    } catch (error) {
      toast.error('Ошибка навигации');
      console.error('Ошибка навигации:', error);
    }
  };
}

Заключение

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

Ключевые выводы включают:

  • Используйте useNavigate() для программной навигации в функциональных компонентах
  • Передавайте объекты location для сложных сценариев навигации с параметрами и состоянием
  • Создавайте компоненты высшего порядка для классовых компонентов, которым нужен доступ к навигации
  • Внимательно обрабатывайте состояние навигации, помня, что оно не сохраняется при обновлении страницы
  • Следуйте современным шаблонам React и используйте TypeScript для лучшей безопасности типов

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

Источники

  1. Документация React Router - хук useNavigate
  2. Руководство по миграции React Router v6
  3. Справочник API React Router
  4. Учебник по программной навигации в React Router v6