Программная навигация с использованием реагирующего маршрутизатора

1433

С помощью react-routerя могу использовать Linkэлемент для создания ссылок, которые изначально обрабатываются реагирующим маршрутизатором.

Я вижу внутренне это звонки this.context.transitionTo(...).

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

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

Джордж Мауэр
источник
26
@SergioTapia имейте в виду, что этот вопрос стоит в одном ряду с основным выпуском response-router, основным выпуском React и основным выпуском javascript
Джордж Мауэр
5
Вот ссылка на учебное пособие в официальных документах о реагирующем маршрутизаторе v4 : acttraining.com/react-router/web/guides/scroll-restoration
chitzui,
4
Вы можете проверить этот ответ stackoverflow.com/questions/44127739/…
Шубхам Хатри
4
Я рад, что вы получили ответ, но мне грустно, что это должно быть так сложно. Навигация в веб-приложении должна быть простой и хорошо документированной. VueJS делает это тривиальным. Каждый компонент получает экземпляр маршрутизатора автоматически. Там есть четко определенный раздел для них в их документах. router.vuejs.org/guide/essentials/navigation.html Мне нравится React для многих вещей, но иногда он бывает слишком сложным. </ rant>
colefner
7
@ GeorgeMauer Ты прав. Это не то, о чем это сообщество. Речь идет об ответах на вопросы, а не о ссорах из-за рамок. Спасибо за напоминание.
Colefner

Ответы:

1438

React Router v5.1.0 с крючками

В useHistoryReact Router> 5.1.0 появился новый хук, если вы используете React> 16.8.0 и функциональные компоненты.

import { useHistory } from "react-router-dom";

function HomeButton() {
  const history = useHistory();

  function handleClick() {
    history.push("/home");
  }

  return (
    <button type="button" onClick={handleClick}>
      Go home
    </button>
  );
}

React Router v4

В версии 4 React Router существует три подхода к программной маршрутизации внутри компонентов.

  1. Используйте withRouterкомпонент высшего порядка.
  2. Используйте композицию и визуализируйте <Route>
  3. Используйте context.

React Router - это в основном оболочка historyбиблиотеки. historyобрабатывает взаимодействие с браузером window.historyдля вас с его браузером и историей хэшей. Он также предоставляет историю памяти, которая полезна для сред, в которых нет глобальной истории. Это особенно полезно при разработке мобильных приложений ( react-native) и модульном тестировании с Node.

historyЭкземпляр имеет два метода для навигации: pushи replace. Если вы рассматриваете historyмассив как посещенные местоположения, pushдобавите новое местоположение в массив и replaceзамените текущее местоположение в массиве новым. Как правило, вы захотите использовать pushметод при навигации.

В более ранних версиях Реагировать маршрутизатор, нужно было создать свой собственный historyэкземпляр, но в v4 <BrowserRouter>, <HashRouter>и <MemoryRouter>компоненты будет создавать браузер, хэш, и экземпляры памяти для вас. React Router делает свойства и методы historyэкземпляра, связанного с вашим маршрутизатором, доступными через контекст под routerобъектом.

1. Используйте withRouterкомпонент высшего порядка

Компонент withRouterвысшего порядка будет вводить historyобъект как опору компонента. Это позволяет получить доступ к pushи replaceметодам без необходимости иметь дело с context.

import { withRouter } from 'react-router-dom'
// this also works with react-router-native

const Button = withRouter(({ history }) => (
  <button
    type='button'
    onClick={() => { history.push('/new-location') }}
  >
    Click Me!
  </button>
))

2. Используйте композицию и визуализируйте <Route>

<Route>Компонент не только для местоположений соответствия. Вы можете отобразить маршрут без маршрута, и он всегда будет соответствовать текущему местоположению . <Route>Компонент проходит тот же реквизит , как withRouter, так что вы сможете получить доступ к historyметодам через historyопору.

import { Route } from 'react-router-dom'

const Button = () => (
  <Route render={({ history}) => (
    <button
      type='button'
      onClick={() => { history.push('/new-location') }}
    >
      Click Me!
    </button>
  )} />
)

3. Используйте контекст *

Но вы, вероятно, не должны

Последний вариант - тот, который вам следует использовать только в том случае, если вы чувствуете себя комфортно в работе с контекстной моделью React (API контекста React стабилен начиная с версии 16).

const Button = (props, context) => (
  <button
    type='button'
    onClick={() => {
      // context.history.push === history.push
      context.history.push('/new-location')
    }}
  >
    Click Me!
  </button>
)

// you need to specify the context type so that it
// is available within the component
Button.contextTypes = {
  history: React.PropTypes.shape({
    push: React.PropTypes.func.isRequired
  })
}

1 и 2 - самый простой вариант реализации, поэтому в большинстве случаев они - ваши лучшие ставки.

Пол С
источник
24
Я попытался использовать метод 1 таким способом withRouter (({history}) => {console.log ("hhhhhhhh"); history.push ('/ bets')}); Но это никогда не работало с роутером 4
Дмитрий Малугин
57
ЧТО!? Я могу просто использовать withRouterвместо передачи historyчерез все мои компоненты? Мне нужно больше времени проводить за чтением документов ...
hellojeffhall
18
Как вы можете просто запустить, history.push('/new-location')не привязывая это поведение к кнопке или другому элементу DOM?
Нил
4
Unexpected use of 'history' no-restricted-globals
выдает
18
contextбольше не является экспериментальным по реакции 16.
trysis
921

Ответ React-Router 5.1.0+ (с использованием хуков и React> 16.8)

Вы можете использовать новый useHistoryхук на функциональных компонентах и ​​программно перемещаться:

import { useHistory } from "react-router-dom";

function HomeButton() {
  let history = useHistory();
  // use history.push('/some/path') here
};

React-Router 4.0.0+ Ответ

В версии 4.0 и выше используйте историю как опору вашего компонента.

class Example extends React.Component {
   // use `this.props.history.push('/some/path')` here
};

ПРИМЕЧАНИЕ: this.props.history не существует, если ваш компонент не был обработан <Route>. Вы должны использовать, <Route path="..." component={YourComponent}/>чтобы иметь this.props.history в YourComponent

React-Router 3.0.0+ Ответ

В версии 3.0 и выше используйте маршрутизатор в качестве опоры для вашего компонента.

class Example extends React.Component {
   // use `this.props.router.push('/some/path')` here
};

React-Router 2.4.0+ Ответ

В версии 2.4 и выше используйте компонент более высокого порядка, чтобы получить маршрутизатор в качестве опоры для вашего компонента.

import { withRouter } from 'react-router';

class Example extends React.Component {
   // use `this.props.router.push('/some/path')` here
};

// Export the decorated class
var DecoratedExample = withRouter(Example);

// PropTypes
Example.propTypes = {
  router: React.PropTypes.shape({
    push: React.PropTypes.func.isRequired
  }).isRequired
};

React-Router 2.0.0+ Ответ

Эта версия обратно совместима с 1.x, поэтому нет необходимости в Руководстве по обновлению. Просто пройтись по примерам должно быть достаточно хорошо.

Тем не менее, если вы хотите переключиться на новый шаблон, внутри маршрутизатора есть модуль browserHistory, к которому вы можете получить доступ с помощью

import { browserHistory } from 'react-router'

Теперь у вас есть доступ к истории браузера, поэтому вы можете выполнять такие операции, как push, replace и т. Д.

browserHistory.push('/some/path')

Дальнейшее чтение: истории и навигация


React-Router 1.xx Ответ

Я не буду вдаваться в детали обновления. Вы можете прочитать об этом в Руководстве по обновлению.

Основное изменение в этом вопросе - это переход от Mixin навигации к истории. Теперь он использует историю браузера API для изменения маршрута, поэтому мы будем использовать его с этого момента pushState().

Вот пример использования Mixin:

var Example = React.createClass({
  mixins: [ History ],
  navigateToHelpPage () {
    this.history.pushState(null, `/help`);
  }
})

Обратите внимание, что это Historyпроисходит из проекта rackt / history . Не от самого React-Router.

Если вы не хотите использовать Mixin по какой-либо причине (возможно, из-за класса ES6), то вы можете получить доступ к истории, полученной от маршрутизатора this.props.history. Он будет доступен только для компонентов, предоставляемых вашим маршрутизатором. Итак, если вы хотите использовать его в любых дочерних компонентах, его нужно передать как атрибут через props.

Вы можете прочитать больше о новой версии в их документации 1.0.x.

Вот справочная страница, посвященная навигации за пределами вашего компонента

Он рекомендует взять ссылку history = createHistory()и обратиться replaceStateк ней.

React-Router 0.13.x Ответ

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

Вот как я это сделал

import React from 'react';
import {Navigation} from 'react-router';

let Authentication = React.createClass({
  mixins: [Navigation],

  handleClick(e) {
    e.preventDefault();

    this.transitionTo('/');
  },

  render(){
    return (<div onClick={this.handleClick}>Click me!</div>);
  }
});

Я был в состоянии звонить transitionTo()без необходимости доступа.context

Или вы можете попробовать модный ES6 class

import React from 'react';

export default class Authentication extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(e) {
    e.preventDefault();

    this.context.router.transitionTo('/');
  }

  render(){
    return (<div onClick={this.handleClick}>Click me!</div>);
  }
}

Authentication.contextTypes = {
  router: React.PropTypes.func.isRequired
};

Реагировать-маршрутизатор-Redux

Примечание: если вы используете Redux, есть еще один проект под названием ReactRouter-Redux , который дает вам Redux привязок для ReactRouter, используя несколько тот же подход , что React-Redux делает

React-Router-Redux имеет несколько доступных методов, которые позволяют легко перемещаться изнутри создателей действий. Они могут быть особенно полезны для людей, которые имеют существующую архитектуру в React Native, и они хотят использовать те же шаблоны в React Web с минимальными дополнительными затратами.

Изучите следующие методы:

  • push(location)
  • replace(location)
  • go(number)
  • goBack()
  • goForward()

Вот пример использования с Redux-Thunk :

./actioncreators.js

import { goBack } from 'react-router-redux'

export const onBackPress = () => (dispatch) => dispatch(goBack())

./viewcomponent.js

<button
  disabled={submitting}
  className="cancel_button"
  onClick={(e) => {
    e.preventDefault()
    this.props.onBackPress()
  }}
>
  CANCEL
</button>
Фелипе Скиннер
источник
3
Я использую, v2.4.0но упомянутый подход не работает для меня, мое приложение вообще не рендерится и выводит консоль: Uncaught TypeError: (0 , _reactRouter.withRouter) is not a functionвот ссылка на мой пост SO: stackoverflow.com/questions/37306166/…
nburk
7
Это должен быть принятый ответ, принятый - немного беспорядок и очень старый. @GiantElk У меня это работает, как описано в ответе для версии 2.6.0.
JMac
2
Этот подход по-прежнему рекомендуется для версии 3.0.x? Многие люди, кажется, используют contextспособ.
ворачивается
6
в 4.0 this.props.history не существует. Что здесь происходит?
Nicolas S.Xu
4
@ NicolasS.Xu this.props.historyне существует, если ваш компонент не был обработан <Route>. Вы должны использовать, <Route path="..." component={YourComponent}/>чтобы иметь this.props.historyв YourComponent.
vogdb
508

React-Router v2

Для самого последнего релиза ( v2.0.0-rc5) рекомендуемый метод навигации заключается в прямом нажатии на одноэлементную историю. Это можно увидеть в действии в документе « Навигация за пределами компонентов» .

Соответствующая выдержка:

import { browserHistory } from 'react-router';
browserHistory.push('/some/path');

Если вы используете более новый API реагирующего маршрутизатора, вам нужно использовать historyfrom from, this.propsкогда внутри компонентов, так:

this.props.history.push('/some/path');

Это также предлагает, pushStateно это устарело за зарегистрированные предупреждения.

При использовании react-router-reduxон предлагает pushфункцию, которую вы можете отправить следующим образом:

import { push } from 'react-router-redux';
this.props.dispatch(push('/some/path'));

Однако это может использоваться только для изменения URL, а не для перехода на страницу.

Бобби
источник
15
Не забывайте, что более новый API не использует, import { browserHistory } from './react-router'а создает историю, используя import createBrowserHistory from 'history/lib/createBrowserHistory'. Позже, вы можете получить доступ historyиз компонентов реквизита:this.props.history('/some/path')
Рикардо Педрони
7
var browserHistory = require('react-router').browserHistory; browserHistory.goBack();
Бобби
7
response-router-redux pushтолько изменяет URL, но фактически не меняет страницу. Чтобы сделать оба, импортировать browserHistoryиз react-routerи использовать browserHistory.push('/my-cool-path'). К сожалению, это не очень легко найти. github.com/reactjs/react-router/blob/master/docs/guides/…
Эндрю Самуэльсен
4
Я использую this.props.history.push ('/'), но изменяется только URL-адрес ... Здесь никакой реальной маршрутизации не происходит. Что мне не хватает?
rgcalsaverini
2
Я просто загрузил свой взгляд на том , как ориентироваться в последней программно react-routerv4
spik3s
56

Вот как вы делаете это react-router v2.0.0с ES6 . react-routerотошел от миксинов.

import React from 'react';

export default class MyComponent extends React.Component {
  navigateToPage = () => {
    this.context.router.push('/my-route')
  };

  render() {
    return (
      <button onClick={this.navigateToPage}>Go!</button>
    );
  }
}

MyComponent.contextTypes = {
  router: React.PropTypes.object.isRequired
}
Алекс Миллер
источник
5
Я полагаю, что текущая рекомендация - использовать historyсинглтон, как заявлено @Bobby. Вы можете использовать, context.routerно вы действительно затрудняете модульное тестирование этих компонентов, поскольку создание экземпляра только того компонента не будет иметь этого в контексте.
Джордж Мауэр
2
@ GeorgeMauer Я думаю, это зависит от того, где работает код. В соответствии с действующими документами рекомендуется использовать context.router, если он запускается на сервере. github.com/reactjs/react-router/blob/master/upgrade-guides/…
KumarM
50

React-Router 4.x Ответ:

С моей стороны, мне нравится иметь единственный объект истории, который я могу нести даже за пределами компонентов. Что мне нравится делать, так это иметь один файл history.js, который я импортирую по требованию, и просто манипулировать им.

Вам просто нужно перейти BrowserRouterна Router и указать реквизиты истории. Это ничего не меняет для вас, за исключением того, что у вас есть свой собственный объект истории, которым вы можете манипулировать, как хотите.

Вам необходимо установить историю , используемую библиотеку react-router.

Пример использования, обозначение ES6:

history.js

import createBrowserHistory from 'history/createBrowserHistory'
export default createBrowserHistory()

BasicComponent.js

import React, { Component } from 'react';
import history from './history';

class BasicComponent extends Component {

    goToIndex(e){
        e.preventDefault();
        history.push('/');
    }

    render(){
        return <a href="#" onClick={this.goToIndex}>Previous</a>;
    }
}

РЕДАКТИРОВАТЬ 16 апреля 2018 года:

Если вам нужно перейти от компонента, который фактически отображается из Routeкомпонента, вы также можете получить доступ к истории из реквизита, например:

BasicComponent.js

import React, { Component } from 'react';

class BasicComponent extends Component {

    navigate(e){
        e.preventDefault();
        this.props.history.push('/url');
    }

    render(){
        return <a href="#" onClick={this.navigate}>Previous</a>;
    }
}
Эрик Мартин
источник
2
За исключением документов React Router, которые говорят нам, что в большинстве случаев вам не нужно использовать Routerвместо BrowserRouter. BrowserRouterсоздает и поддерживает historyобъект для вас. Вы не должны по умолчанию создавать свои собственные, только если вы [«нуждаетесь в глубокой интеграции с такими инструментами управления состоянием, как Redux.» ) Acttraining.com/react-router/web/api/Router
bobbyz
2
Это то, что я узнал со временем и опытом. Хотя обычно рекомендуется использовать значение по умолчанию this.props.history, я пока не нашел решения, которое могло бы помочь мне сделать такую ​​вещь в классе, который не является компонентом или любым другим инструментом, который не создан как компонент React, для которого Вы можете передать реквизит. Спасибо за ваш отзыв :)
Эрик Мартин
44

Для этого, кто не контролирует серверную часть и из-за этого использует hash router v2:

Поместите свою историю в отдельный файл (например, app_history.js ES6):

import { useRouterHistory } from 'react-router'
import { createHashHistory } from 'history'
const appHistory = useRouterHistory(createHashHistory)({ queryKey: false });

export default appHistory;

И используйте это везде!

Ваша точка входа для реакции-маршрутизатора (app.js ES6):

import React from 'react'
import { render } from 'react-dom'
import { Router, Route, Redirect } from 'react-router'
import appHistory from './app_history'
...
const render((
  <Router history={appHistory}>
  ...
  </Router>
), document.querySelector('[data-role="app"]'));

Ваша навигация внутри любого компонента (ES6):

import appHistory from '../app_history'
...
ajaxLogin('/login', (err, data) => {
  if (err) {
    console.error(err); // login failed
  } else {
    // logged in
    appHistory.replace('/dashboard'); // or .push() if you don't need .replace()
  }
})
Алексей Володько
источник
1
Спасибо. Может быть, я что-то упустил. Разве это не то же самое, что два верхних ответа?
Джордж Мауэр
2
Когда вы используете навигацию «по хешу», в «реакции-маршрутизаторе» v2 вам нужно установить дополнительный модуль «история» (если вы хотите избежать странных запросов к хешу), и вместо использования browserHistory вам нужно использовать useRouterHistory (createHashHistory) ( {queryKey: false}). Это главная причина, почему я написал это. Я показал, как ориентироваться в истории хешей.
Алексей Володько
1
О, я вижу, вы правы. Я больше не участвую в этом проекте, но думаю, что сейчас предпочтительнее использовать historyлюбой из этих подходов
Джордж Мауэр
4
почему этот ответ не самый лучший? почему все проблемы с прототипами Context, HOC или событий, почему бы просто не импортировать историю и использовать где угодно? просто пытаюсь понять компромисс
storm_buster
@storm_buster нет компромисса. У нас есть модули ES6, и мы должны их использовать! Это типичная демонстрация использования вашего инструмента (в данном случае реагирования) для всего, даже для вещей, для которых вы не хотите их использовать.
Капай
40

React Router V4

ТЛ: д-р;

if (navigate) {
  return <Redirect to="/" push={true} />
}

Простой и декларативный ответ заключается в том, что вам нужно использовать <Redirect to={URL} push={boolean} />в сочетании сsetState()

push: boolean - при значении true перенаправление будет помещать новую запись в историю вместо замены текущей.


import { Redirect } from 'react-router'

class FooBar extends React.Component {
  state = {
    navigate: false
  }

  render() {
    const { navigate } = this.state

    // here is the important part
    if (navigate) {
      return <Redirect to="/" push={true} />
    }
   // ^^^^^^^^^^^^^^^^^^^^^^^

    return (
      <div>
        <button onClick={() => this.setState({ navigate: true })}>
          Home
        </button>
      </div>
    )
  }
}

Полный пример здесь . Узнайте больше здесь .

PS. В примере используются инициализаторы свойств ES7 + для инициализации состояния. Посмотри здесь , если тебе интересно.

Любомир
источник
Это лучший ответ, который я нашел. Использование withRouterне работало, когда уже на маршруте, который перезагружается. В нашем случае нам пришлось выборочно делать setState(вызывая return <Redirect>), если уже на маршруте или history.push()из другого места.
Кармаказе
Очень, очень умный. И все же очень просто. Tks. Я использовал его внутри ReactRouterTable, поскольку хотел обрабатывать щелчок по всей строке. Используется на onRowClick для установки состояния. setState ({navigateTo: "/ path /" + row.uuid});
Ловато
это пуленепробиваемый ответ.
Нитин Джадхав
31

Предупреждение: этот ответ охватывает только версии ReactRouter до 1.0

Я обновлю этот ответ вариантами использования 1.0.0-rc1 после!

Вы можете сделать это и без миксинов.

let Authentication = React.createClass({
  contextTypes: {
    router: React.PropTypes.func
  },
  handleClick(e) {
    e.preventDefault();
    this.context.router.transitionTo('/');
  },
  render(){
    return (<div onClick={this.handleClick}>Click me!</div>);
  }
});

Суть в том, что он недоступен, если вы не определили contextTypesкласс.

Что касается контекста, то это объект, подобный реквизиту, который передается от родителя к потомку, но он передается неявно, без необходимости каждый раз переопределять реквизиты. См. Https://www.tildedave.com/2014/11/15/introduction-to-contexts-in-react-js.html.

Qiming
источник
26

Я попробовал по крайней мере 10 способов сделать это, прежде чем что-то заработало!

Ответ @Felipe Skinner withRouterбыл немного ошеломляющим для меня, и я не был уверен, что хочу делать новые имена классов "ExportedWithRouter".

Вот самый простой и понятный способ сделать это, около React-Router 3.0.0 и ES6:

React-Router 3.xx с ES6:

import { withRouter } from 'react-router';

class Example extends React.Component {
   // use `this.props.router.push('/some/path')` here
};

// Export the decorated class
export default withRouter(Example);

или, если это не ваш класс по умолчанию, экспортируйте как:

withRouter(Example);
export { Example };

Обратите внимание, что в 3.xx сам <Link>компонент использует router.push, так что вы можете передать ему все, что вы передадите <Link to=тегу, например:

   this.props.router.push({pathname: '/some/path', query: {key1: 'val1', key2: 'val2'})'
Бен Уилер
источник
1
Работает совершенно нормально. Но отвечает 200 OKвместо 30xкода. Как я могу исправить эту проблему?
Мухаммед Атиек Азам
1
Точно сказать не могу. Мне это никогда не удавалось, потому что это не внешний запрос - это просто ваш браузер, использующий собственный код страницы, чтобы изменить то, что на странице. Вы всегда можете сделать ручное перенаправление JavaScript, если хотите.
Бен Уилер
1
его также можно использовать с @withRouter в качестве декоратора классов ... отлично подходит для mobx
Дан
21

Чтобы выполнить навигацию программным способом , вам нужно добавить новую историю в props.history в вашей папке component, чтобы что-то вроде этого могло помочь вам:

//using ES6
import React from 'react';

class App extends React.Component {

  constructor(props) {
    super(props)
    this.handleClick = this.handleClick.bind(this)
  }

  handleClick(e) {
    e.preventDefault()
    /* Look at here, you can add it here */
    this.props.history.push('/redirected');
  }

  render() {
    return (
      <div>
        <button onClick={this.handleClick}>
          Redirect!!!
        </button>
      </div>
    )
  }
}

export default App;
Алиреза
источник
Пожалуйста, напишите версию реакции-роутера
Ash
19

Для компонентов ES6 + React у меня сработало следующее решение.

Я последовал за Фелиппом Скиннером, но добавил комплексное решение, чтобы помочь таким начинающим, как я.

Ниже приведены версии, которые я использовал:

«реагирующий маршрутизатор»: «^ 2.7.0»

«реагировать»: «^ 15.3.1»

Ниже приведен мой компонент реакции, где я использовал программную навигацию с использованием реагирующего маршрутизатора:

import React from 'react';

class loginComp extends React.Component {
   constructor( context) {
    super(context);
    this.state = {
      uname: '',
      pwd: ''
    };
  }

  redirectToMainPage(){
        this.context.router.replace('/home');
  }

  render(){
    return <div>
           // skipping html code 
             <button onClick={this.redirectToMainPage.bind(this)}>Redirect</button>
    </div>;
  }
};

 loginComp.contextTypes = {
    router: React.PropTypes.object.isRequired
 }

 module.exports = loginComp;

Ниже приведена конфигурация для моего маршрутизатора:

 import { Router, Route, IndexRedirect, browserHistory } from 'react-router'

 render(<Router history={browserHistory}>
          <Route path='/' component={ParentComp}>
            <IndexRedirect to = "/login"/>
            <Route path='/login' component={LoginComp}/>
            <Route path='/home' component={HomeComp}/>
            <Route path='/repair' component={RepairJobComp} />
            <Route path='/service' component={ServiceJobComp} />
          </Route>
        </Router>, document.getElementById('root'));
Softwareddy
источник
19

Возможно, это не лучший подход, но ... С помощью response-router v4 следующий Typescript может дать представление для некоторых.

В представленном ниже компоненте, например LoginPage, routerобъект доступен и просто вызывается router.transitionTo('/homepage')для навигации.

Навигационный код был взят из .

"react-router": "^4.0.0-2", "react": "^15.3.1",

import Router from 'react-router/BrowserRouter';
import { History } from 'react-history/BrowserHistory';
import createHistory from 'history/createBrowserHistory';
const history = createHistory();

interface MatchWithPropsInterface {
  component: typeof React.Component,
  router: Router,
  history: History,
  exactly?: any,
  pattern: string
}

class MatchWithProps extends React.Component<MatchWithPropsInterface,any> {
  render() {
    return(
      <Match {...this.props} render={(matchProps) => (
             React.createElement(this.props.component, this.props)

        )}
       />
    )
  }
}

ReactDOM.render(
    <Router>
      {({ router }) => (
        <div>
          <MatchWithProps exactly pattern="/" component={LoginPage} router={router} history={history} />
          <MatchWithProps pattern="/login" component={LoginPage} router={router} history={history} />
          <MatchWithProps pattern="/homepage" component={HomePage} router={router} history={history} />
          <Miss component={NotFoundView} />
        </div>
      )}
    </Router>,

   document.getElementById('app')
);

mcku
источник
1
Почему вы не используете withRouter и browserHistory?
Эрвин Майер
1
@ Erwin API отличается в v4. Смотрите этот github.com/ReactTraining/react-router/issues/3847 .
Маку
1
@mcku Откуда вы взяли свой dts-файл машинописного текста для response-router v4?
Jmathew
1
@jmathew У меня нет определений типов для
response
16

В React-Router v4 и ES6

Вы можете использовать withRouterи this.props.history.push.

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

class Home extends Component {

    componentDidMount() {
        this.props.history.push('/redirect-to');
    }
}

export default withRouter(Home);
Хоссейн
источник
2
Уже упоминалось в нескольких других ответах (в том числе подробно в верхнем ответе)
Джордж Мауэр
13

Чтобы использовать withRouterс компонентом на основе классов, попробуйте что-то вроде этого ниже. Не забудьте изменить оператор экспорта для использования withRouter:

import { withRouter } from 'react-router-dom'

class YourClass extends React.Component {
  yourFunction = () => {
    doSomeAsyncAction(() =>
      this.props.history.push('/other_location')
    )
  }

  render() {
    return (
      <div>
        <Form onSubmit={ this.yourFunction } />
      </div>
    )
  }
}

export default withRouter(YourClass);
Янош
источник
11

основываясь на предыдущем ответе
Хосе Антонио Постиго и Бена Уилера
о новизне? должен быть написан на Typescript
и использование декораторов
ИЛИ статическое свойство / поле

import * as React from "react";
import Component = React.Component;
import { withRouter } from "react-router";

export interface INavigatorProps {
    router?: ReactRouter.History.History;
}

/**
 * Note: goes great with mobx 
 * @inject("something") @withRouter @observer
 */
@withRouter
export class Navigator extends Component<INavigatorProps, {}>{
    navigate: (to: string) => void;
    constructor(props: INavigatorProps) {
        super(props);
        let self = this;
        this.navigate = (to) => self.props.router.push(to);
    }
    render() {
        return (
            <ul>
                <li onClick={() => this.navigate("/home")}>
                    Home
                </li>
                <li onClick={() => this.navigate("/about")}>
                    About
                </li>
            </ul>
        )
    }
}

/**
 * Non decorated 
 */
export class Navigator2 extends Component<INavigatorProps, {}> {

    static contextTypes = {
        router: React.PropTypes.object.isRequired,
    };

    navigate: (to: string) => void;
    constructor(props: INavigatorProps, context: any) {
        super(props, context);
        let s = this;
        this.navigate = (to) =>
            s.context.router.push(to);
    }
    render() {
        return (
            <ul>
                <li onClick={() => this.navigate("/home")}>
                    Home
                </li>
                <li onClick={() => this.navigate("/about")}>
                    About
                </li>
            </ul>
        )
    }
}

с любым установленным сегодня npm. «response-router»: «^ 3.0.0» и
«@ types / реагирующий маршрутизатор»: «^ 2.0.41»

Дэн
источник
11

с React-Router v4 на горизонте, теперь есть новый способ сделать это.

import { MemoryRouter, BrowserRouter } from 'react-router';

const navigator = global && global.navigator && global.navigator.userAgent;
const hasWindow = typeof window !== 'undefined';
const isBrowser = typeof navigator !== 'undefined' && navigator.indexOf('Node.js') === -1;
const Router = isBrowser ? BrowserRouter : MemoryRouter;

<Router location="/page-to-go-to"/>

response-lego - это пример приложения, которое показывает, как использовать / обновлять реактив-маршрутизатор, и включает примеры функциональных тестов, которые перемещаются по приложению.

peter.mouland
источник
5
Это отлично подходит для навигации из функции рендера, хотя мне интересно, как перейти от чего-то вроде ловушки жизненного цикла или избыточности?
Рубен Мартинес младший
11

В реакции роутера v4. Я следую этому двухстороннему маршруту программно.

1. this.props.history.push("/something/something")
2. this.props.history.replace("/something/something")

Номер два

Заменяет текущую запись в стеке истории

Чтобы получить историю в реквизитах, вам, возможно, придется обернуть свой компонент с

withRouter

saiful619945
источник
Спасибо!! Лучший ответ
Таха Фаруки
9

Если вы используете хеш или историю браузера, то вы можете сделать

hashHistory.push('/login');
browserHistory.push('/login');
Заман Афзал
источник
2
hashHistory.push, дает не может прочитать свойство "push" из неопределенного. Откуда вы их импортируете?
ArchNoob
import {hashHistory} из "реакции-маршрутизатора"; Вы можете импортировать его таким образом вверху.
Заман Афзал
Я использую ваш пример кода из моего файла SAG-файла, импортируемого так же, как вы сказали, и использую его, но я получаю эту ошибку TypeError.
ArchNoob
8

С текущей версией React (15.3) this.props.history.push('/location');у меня сработало, но показало следующее предупреждение:

browser.js: 49 Предупреждение: [реакции-маршрутизатор] props.historyи context.historyустарели. Пожалуйста, используйте context.router.

и я решил это с помощью context.routerэтого:

import React from 'react';

class MyComponent extends React.Component {

    constructor(props) {
        super(props);
        this.backPressed = this.backPressed.bind(this);
    }

    backPressed() {
        this.context.router.push('/back-location');
    }

    ...
}

MyComponent.contextTypes = {
    router: React.PropTypes.object.isRequired
};

export default MyComponent;
Хосе Антонио Постиго
источник
1
Это работает для V4. Ну ... близко ... это должно быть this.context.router.history.push ('/ back-location');
веломоме
7

React-Router V4

если вы используете версию 4, то вы можете использовать мою библиотеку (плагин Shameless), где вы просто отправляете действие, и все просто работает!

dispatch(navigateTo("/aboutUs"));

trippler

Гарри Тейлор
источник
5

Те, кто сталкивается с проблемами в реализации этого на реакции-маршрутизатор v4.

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

history.js

import createHistory from 'history/createBrowserHistory'

export default createHistory()

App.js / Route.jsx

import { Router, Route } from 'react-router-dom'
import history from './history'
...
<Router history={history}>
 <Route path="/test" component={Test}/>
</Router>

another_file.js ИЛИ редукционный файл

import history from './history' 

history.push('/test') // this should change the url and re-render Test component

Все благодаря этому комментарию: ReactTraining выпускает комментарий

reflexgravity
источник
4

Если случится с парой RR4 с редуксом черезact -router-redux , используйте react-router-reduxвариант действий создателей маршрутизации от .

import { push, replace, ... } from 'react-router-redux'

class WrappedComponent extends React.Component {
  handleRedirect(url, replaceState = true) { 
    replaceState 
      ? this.props.dispatch(replace(url)) 
      : this.props.dispatch(push(url)) 
  }
  render() { ... }
}

export default connect(null)(WrappedComponent)

Если для управления асинхронным потоком используется redux thunk / saga, лучше импортируйте вышеуказанные создатели действий в действиях redux и подключайте для реагирования компоненты, используя mapDispatchToProps.

Xlee
источник
1
react-router-reduxдавно устарел / архивирован
oligofren
4

Вы также можете использовать useHistoryхук в компоненте без сохранения состояния. Пример из документов.

import { useHistory } from "react-router"

function HomeButton() {
  const history = useHistory()

  return (
    <button type="button" onClick={() => history.push("/home")}>
      Go home
    </button>
  )
}

Примечание: крючки были добавлены react-router@5.1.0и требуютreact@>=16.8

Nickofthyme
источник
1
Хороший звонок, не могли бы вы отметить, какая версия реакции-маршрутизатора и реагирует, что относится? Это новое изменение, которое не всегда было доступно
Джордж Мауэр
3

Правильный ответ был для меня на момент написания

this.context.router.history.push('/');

Но вам нужно добавить PropTypes в ваш компонент

Header.contextTypes = {
  router: PropTypes.object.isRequired
}
export default Header;

Не забудьте импортировать PropTypes

import PropTypes from 'prop-types';
веб-мастер
источник
Пожалуйста, запишите версию реакции-роутера, и что вы импортировали из реакции-роутера
Ash
Ничего не импортировать, и это версия «Reaction-router»: «^ 4.2.0»
веб-мастер
3

Возможно, не лучшее решение, но оно выполняет свою работу:

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

// create functional component Post
export default Post = () => (
    <div className="component post">

        <button className="button delete-post" onClick={() => {
            // ... delete post
            // then redirect, without page reload, by triggering a hidden Link
            document.querySelector('.trigger.go-home').click();
        }}>Delete Post</button>

        <Link to="/" className="trigger go-home hidden"></Link>

    </div>
);

По сути, логика, связанная с одним действием (в данном случае удаление сообщения), в итоге вызовет триггер для перенаправления. Это не идеально, потому что вы добавите триггер DOM-узла к вашей разметке, чтобы вы могли удобно вызывать его при необходимости. Кроме того, вы будете напрямую взаимодействовать с DOM, что в компоненте React может оказаться нежелательным.

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

neatsu
источник
Можете ли вы объяснить, почему вы бы использовали это по сравнению с принятым решением?
Джордж Мауэр
Это просто альтернативное решение обсуждаемой проблемы. Как уже говорилось, не идеально, но он прекрасно работает для программных перенаправлений.
neatsu
2

Для меня это сработало, особого импорта не требовалось:

<input 
  type="button" 
  name="back" 
  id="back" 
  class="btn btn-primary" 
  value="Back" 
  onClick={() => { this.props.history.goBack() }} 
/>
JJ_Coder4Hire
источник
Конечно, это требует специального импорта - ваш компонент не был historyдобавлен propsсам по себе. Это происходит из HoC, который вам необходимо импортировать, чтобы использовать (компоненты, отправленные напрямую Route, автоматически оборачиваются этим HoC, но затем вам нужен импорт для Route...)
Джордж Мауэр
2

вместо этого используйте hookrouter, современную альтернативу реакции-маршрутизатора

https://www.npmjs.com/package/hookrouter

import { useRoutes, usePath, A} from "hookrouter";

чтобы ответить на вопрос OP о связывании через поле выбора, вы можете сделать это:

navigate('/about');
StefanBob
источник
FYI. Сам автор называет эту библиотеку «доказательством концепции», и она почти год не работала (июнь 2019 года).
MEMark
@MEMark, потому что он уже идеален
StefanBob
Хорошо, 38 открытых вопросов, вместе с тем фактом, что версия 2.0 будет полностью переписана согласно автору, говорит об обратном.
MEMark
1

Для React Router v4 +

Предполагая, что вам не нужно будет перемещаться во время самого начального рендера (для которого вы можете использовать <Redirect> компонент), это то, что мы делаем в нашем приложении.

Определите пустой маршрут, который возвращает ноль, это позволит вам получить доступ к объекту истории. Вы должны сделать это на верхнем уровне, где вашRouter определяется.

Теперь вы можете делать все, что можно сделать в истории, как history.push(), history.replace()и history.go(-1)т. Д!

import React from 'react';
import { HashRouter, Route } from 'react-router-dom';

let routeHistory = null;

export function navigateTo(path) {
  if(routeHistory !== null) {
    routeHistory.push(path);
  }
}

export default function App(props) {
  return (
    <HashRouter hashType="noslash">
      <Route
        render={({ history }) => {
          routeHistory = history;
          return null;
        }}
      />
      {/* Rest of the App */}
    </HashRouter>
  );
}
Афтаб Хан
источник
1

response-router-dom: 5.1.2

  • Этот сайт имеет 3 страницы, все они динамически отображаются в браузере.

  • Хотя страница никогда не обновляется, обратите внимание, как React Router обновляет URL-адрес при переходе по сайту. Это сохраняет историю браузера, гарантируя, что такие вещи, как кнопка «Назад» и закладки работают правильно

  • Коммутатор просматривает все его дочерние элементы и делает первый , чей путь совпадает с текущим URL. Используйте любое время, когда у вас есть несколько маршрутов, но вы хотите, чтобы только один из них отображался одновременно

import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";



export default function BasicExample() {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
          <li>
            <Link to="/dashboard">Dashboard</Link>
          </li>
        </ul>

        <hr />

        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

// You can think of these components as "pages"
// in your app.

function Home() {
  return (
    <div>
      <h2>Home</h2>
    </div>
  );
}

function About() {
  return (
    <div>
      <h2>About</h2>
    </div>
  );
}

function Dashboard() {
  return (
    <div>
      <h2>Dashboard</h2>
    </div>
  );
}```
GOVINDARAJ
источник
Добро пожаловать на ТАК! Когда вы отвечаете на вопрос с таким количеством ответов, постарайтесь показать свои преимущества.
Дэвид Гарсия Бодего
Это также не отвечает на вопрос, как сделать это программно из императивного js, а не с разметкой.
Джордж Мауэр
1

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

Используя следующие версии:

реагировать: 16.13.1

реакция-дом: 16.13.1

реакции-маршрутизатор: 5.2.0

response-router-dom: 5.2.0

машинопись: 3.7.2

Конфигурация:

Поэтому в первую очередь используется решение HashRouter, настроенное следующим образом:

<HashRouter>
    // ... buttons for redirect

    <Switch>
      <Route exact path="/(|home)" children={Home} />
      <Route exact path="/usehistory" children={UseHistoryResult} />
      <Route exact path="/withrouter" children={WithRouterResult} />
      <Route exact path="/redirectpush" children={RedirectPushResult} />
      <Route children={Home} />
    </Switch>
</HashRouter>

Из документации о <HashRouter>:

Объект, <Router>который использует хэш-часть URL-адреса (т. Е. window.location.hash), Чтобы синхронизировать пользовательский интерфейс с URL-адресом.

Решения:

  1. Используя, <Redirect>чтобы нажать используя useState:

Используя в функциональном компоненте ( RedirectPushActionкомпонент из моего хранилища) мы можем использовать useStateдля обработки перенаправления. Сложность в том, что как только произошло перенаправление, нам нужно redirectвернуть состояние обратно false. Используя setTimeOutс 0задержкой, мы ждем, пока React совершитRedirect выполнит в DOM, а затем возвращаем кнопку, чтобы использовать в следующий раз.

Пожалуйста, найдите мой пример ниже:

const [redirect, setRedirect] = useState(false);
const handleRedirect = useCallback(() => {
    let render = null;
    if (redirect) {
        render = <Redirect to="/redirectpush" push={true} />

        // in order wait until commiting to the DOM
        // and get back the button for clicking next time
        setTimeout(() => setRedirect(false), 0);
    }
    return render;
}, [redirect]);

return <>
    {handleRedirect()}
    <button onClick={() => setRedirect(true)}>
        Redirect push
    </button>
</>

Из <Redirect>документации:

Рендеринг <Redirect>переместится на новое место. Новое местоположение переопределит текущее местоположение в стеке истории, как перенаправления на стороне сервера (HTTP 3xx).

  1. Используя useHistoryкрючок:

В моем решении есть компонент, UseHistoryActionкоторый называется следующим:

let history = useHistory();

return <button onClick={() => { history.push('/usehistory') }}>
    useHistory redirect
</button>

useHistoryКрюк дает нам доступ к объекту истории , который помогает нам программно или навигации по изменению маршрутов.

  1. Используя withRouter, получить historyот props:

Создан один компонент под названием WithRouterAction, отображается как показано ниже:

const WithRouterAction = (props:any) => {
    const { history } = props;

    return <button onClick={() => { history.push('/withrouter') }}>
        withRouter redirect
    </button>
}

export default withRouter(WithRouterAction);

Чтение из withRouterдокументации:

Вы можете получить доступ к historyсвойствам объекта и соответствию ближайшего <Route>через withRouterкомпонент высшего порядка. withRouterПройдут обновляется match, locationи historyреквизит к обернутой компонента всякий раз , когда он делает.

Демо-версия:

Для лучшего представления я создал репозиторий GitHub с этими примерами, пожалуйста, найдите его ниже:

https://github.com/norbitrial/react-router-programmatically-redirect-examples

Надеюсь, это поможет!

norbitrial
источник