Nawiguj programowo za pomocą routera reagującego


1427

Za react-routerpomocą tego Linkelementu mogę tworzyć linki, które są natywnie obsługiwane przez router reagujący.

Widzę, że to wewnętrzne połączenia this.context.transitionTo(...).

Chcę zrobić nawigację. Nie z linku, ale z rozwijanego wyboru (jako przykład). Jak mogę to zrobić w kodzie? Co to jest this.context?

Widziałem Navigationmixin, ale czy mogę to zrobić bez mixins?


26
@SergioTapia pamiętaj, że to pytanie dotyczy głównej wersji React-Routera, głównej wersji React ORAZ głównej wersji javascript
George Mauer

5
Oto link do samouczka w oficjalnych dokumentach dotyczących reakcji routera v4: reagtraining.com/react-router/web/guides/scroll-restoration
chitzui

4
Możesz sprawdzić tę odpowiedź stackoverflow.com/questions/44127739/...
Shubham Khatri

4
Cieszę się, że otrzymałeś odpowiedź, ale smutno, że to musi być tak skomplikowane. Nawigacja w aplikacji internetowej powinna być łatwa i dobrze udokumentowana. VueJS sprawia, że ​​jest to banalne. Każdy komponent dostaje się automatycznie do instancji routera. W ich dokumentach jest wyraźnie określona sekcja. router.vuejs.org/guide/essentials/navigation.html Lubię React dla wielu rzeczy, ale czasami jest to zbyt skomplikowane. </rant>
colefner

7
@GeorgeMauer Masz rację. Nie o to chodzi w tej społeczności. Chodzi o odpowiadanie na pytania, a nie sprzeczanie się o ramy. Dzięki za przypomnienie.
colefner

Odpowiedzi:


1427

Reaguj router wer. 5.1.0 z hakami

W useHistoryReact Router> 5.1.0 pojawi się nowy haczyk, jeśli używasz React> 16.8.0 i komponentów funkcjonalnych.

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

W wersji 4 React Router istnieją trzy podejścia do zautomatyzowanego routingu w ramach komponentów.

  1. Użyj withRouterkomponentu wyższego rzędu.
  2. Użyj kompozycji i renderuj a <Route>
  3. Użyj context.

React Router to głównie opakowanie wokół historybiblioteki. historyobsługuje interakcje z przeglądarkami window.historyza pomocą przeglądarki i historii skrótów. Zapewnia również historię pamięci, która jest przydatna w środowiskach, które nie mają historii globalnej. Jest to szczególnie przydatne w tworzeniu aplikacji mobilnych ( react-native) i testowaniu jednostkowym za pomocą Node.

historyPrzykład dwie metody nawigacji: pushi replace. Jeśli uważasz, że historyjest to tablica odwiedzanych lokalizacji, pushdoda nową lokalizację do tablicy i replacezastąpi bieżącą lokalizację w tablicy nową. Zazwyczaj będziesz chciał użyć tej pushmetody podczas nawigacji.

We wcześniejszych wersjach React routera, trzeba było stworzyć własną historyinstancję, ale w V4 <BrowserRouter>, <HashRouter>i <MemoryRouter>składniki będą tworzyć przeglądarce, ziemniaczane i instancje pamięci dla ciebie. React Router udostępnia właściwości i metody historyinstancji powiązanej z routerem za pośrednictwem kontekstu, pod routerobiektem.

1. Użyj withRouterkomponentu wyższego rzędu

withRouterSkładnika wyższego rzędu wstrzyknie historyobiekt jako podpora elementu. Umożliwia to dostęp do metod pushi replacebez konieczności zajmowania się nimi 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. Użyj kompozycji i renderuj <Route>

<Route>Składnikiem jest nie tylko dla miejsc dopasowywania. Możesz wyrenderować trasę bez ścieżki , która zawsze będzie pasować do bieżącej lokalizacji . <Route>Komponent przechodzi te same rekwizyty jak withRouter, dzięki czemu będą mogli uzyskać dostęp do historymetod przez historypodpory.

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

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

3. Użyj kontekstu *

Ale prawdopodobnie nie powinieneś

Ostatnią opcją jest ta, z której powinieneś korzystać tylko wtedy, gdy czujesz się komfortowo pracując z modelem kontekstowym React (Context API React jest stabilny od wersji 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 i 2 to najprostsze opcje do wdrożenia, więc w większości przypadków są to najlepsze zakłady.


24
Próbowałem użyć metody 1 w ten sposób z routerem (({historia}) => {console.log ("hhhhhhhh"); history.push ('/ bets')}); Ale to nigdy nie działało z routerem 4
Dmitry Malugin

57
CO!? Mogę po prostu użyć withRouterzamiast przejść historyprzez wszystkie moje komponenty? Gahh Muszę spędzać więcej czasu na czytaniu dokumentów ...
hellojeffhall

18
Jak możesz uruchomić history.push('/new-location')bez dołączania tego zachowania do przycisku lub innego elementu DOM?
Neil

4
błąd rzucania jakoUnexpected use of 'history' no-restricted-globals
Pardeep Jain

18
contextnie jest już eksperymentalny od reakcji 16.
trysis

920

Odpowiedź React-Router 5.1.0+ (za pomocą haków i React> 16,8)

Możesz użyć nowego useHistoryzaczepu w elementach funkcjonalnych i programowo nawigować:

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

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

React-Router 4.0.0+ Odpowiedź

W wersji 4.0 i nowszych użyj historii jako rekwizytu swojego komponentu.

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

UWAGA: this.props.history nie istnieje w przypadku, gdy twój komponent nie był renderowany <Route>. Powinieneś użyć, <Route path="..." component={YourComponent}/>aby mieć this.props.history w YourComponent

React-Router 3.0.0+ odpowiedzi

W wersji 3.0 i nowszej użyj routera jako rekwizytu swojego komponentu.

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

React-Router 2.4.0+ Odpowiedź

W wersji 2.4 i nowszej użyj komponentu wyższego rzędu, aby uzyskać router jako rekwizyt komponentu.

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+ Odpowiedź

Ta wersja jest wstecznie kompatybilna z wersją 1.x, więc nie ma potrzeby Przewodnika aktualizacji. Samo przejrzenie przykładów powinno wystarczyć.

To powiedziawszy, jeśli chcesz przełączyć się na nowy wzorzec, w routerze znajduje się moduł browserHistory, do którego masz dostęp

import { browserHistory } from 'react-router'

Teraz masz dostęp do historii przeglądarki, dzięki czemu możesz wykonywać takie czynności, jak wypychanie, zastępowanie itp. Jak:

browserHistory.push('/some/path')

Dalsza lektura: Historie i nawigacja


React-Router 1.xx Odpowiedź

Nie będę wchodził w szczegóły aktualizacji. Możesz o tym przeczytać w Przewodniku aktualizacji

Główną zmianą w tym pytaniu jest zmiana z Mixinu nawigacji na Historię. Teraz używa interfejsu API historii przeglądarki do zmiany trasy, więc będziemy używać pushState()odtąd.

Oto przykład użycia Mixin:

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

Zauważ, że Historypochodzi to z projektu rack / history . Nie z samego React-Routera.

Jeśli z jakiegoś powodu nie chcesz używać Mixin (być może z powodu klasy ES6), możesz uzyskać dostęp do historii, którą otrzymujesz z routera this.props.history. Będzie dostępny tylko dla komponentów renderowanych przez router. Tak więc, jeśli chcesz go używać w komponentach potomnych, musisz przekazać go jako atrybut przez props.

Możesz przeczytać więcej o nowej wersji w dokumentacji 1.0.x.

Oto strona pomocy dotycząca nawigacji poza komponentem

Zaleca złapanie referencji history = createHistory()i wywołanie replaceStatetego.

React-Router 0.13.x Odpowiedź

Wpadłem w ten sam problem i mogłem znaleźć rozwiązanie tylko dzięki mixinowi Nawigacji, który jest dostarczany z routerem reagującym.

Oto jak to zrobiłem

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

Mogłem zadzwonić transitionTo()bez potrzeby dostępu.context

Lub możesz wypróbować fantazyjny 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
};

React-Router-Redux

Uwaga: jeśli używasz Redux, istnieje inny projekt o nazwie ReactRouter-Redux że daje Redux wiązania dla ReactRouter, używając nieco takie samo podejście, które React-Redux robi

React-Router-Redux ma kilka dostępnych metod, które pozwalają na łatwą nawigację z poziomu kreatorów akcji wewnętrznych. Mogą być one szczególnie przydatne dla osób, które mają istniejącą architekturę w React Native i chcą korzystać z tych samych wzorców w React Web przy minimalnym nakładzie pracy.

Poznaj następujące metody:

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

Oto przykład użycia z 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
Używam, v2.4.0ale wspomniane podejście nie działa dla mnie, moja aplikacja w ogóle się nie wyświetla, a wyniki konsoli: Uncaught TypeError: (0 , _reactRouter.withRouter) is not a functionoto link do mojego postu SO: stackoverflow.com/questions/37306166/…
nburk

7
To powinna być zaakceptowana odpowiedź, zaakceptowana jest trochę bałaganu i jest bardzo stara. @GiantElk Mam to działa zgodnie z opisem w odpowiedzi na wersję 2.6.0.
JMac,

2
Czy to podejście jest nadal zalecanym sposobem dla wersji 3.0.x? Wydaje się, że wiele osób korzysta z tej contextdrogi.
velop,

6
w 4.0, this.props.history nie istnieje. Co się dzieje?
Nicolas S.Xu,

4
@ NicolasS.Xu this.props.historynie istnieje w przypadku, gdy twój komponent nie był renderowany <Route>. Należy użyć <Route path="..." component={YourComponent}/>, aby this.props.historyw YourComponent.
vogdb

508

React-Router v2

W najnowszej wersji ( v2.0.0-rc5) zalecaną metodą nawigacji jest bezpośrednie przejście do singletonu historii. Możesz to zobaczyć w akcji w dokumencie Nawigowanie poza dokumentem Składniki .

Odpowiedni fragment:

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

W przypadku korzystania z nowszej reagować-Router API, należy skorzystać z poniższych historyod this.propskiedy wewnątrz elementów tak:

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

Oferuje również, pushStateale jest to przestarzałe dla zarejestrowanych ostrzeżeń.

Jeśli używasz react-router-redux, oferuje pushfunkcję, którą możesz wysłać w następujący sposób:

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

Można to jednak wykorzystać tylko do zmiany adresu URL, a nie do nawigacji na stronie.


15
Nie zapominaj, że nowszy interfejs API nie używa, import { browserHistory } from './react-router'ale zamiast tego tworzy historię import createBrowserHistory from 'history/lib/createBrowserHistory'. Później można uzyskać dostęp historyz rekwizytów komponentów:this.props.history('/some/path')
Ricardo Pedroni

7
var browserHistory = require('react-router').browserHistory; browserHistory.goBack();
Bobby

7
Reakcja-router-redux pushzmienia tylko adres URL, w rzeczywistości nie zmienia strony. Aby to zrobić, zaimportuj browserHistoryzi react-routerużyj browserHistory.push('/my-cool-path'). Niestety nie jest to bardzo łatwe do znalezienia. github.com/reactjs/react-router/blob/master/docs/guides/…
Andrew Samuelsen

4
Korzystam z this.props.history.push ('/'), ale zmienia się tylko adres URL ... Nie ma tu faktycznego routingu. czego mi brakuje?
rgcalsaverini

2
Właśnie przesłałem swoje podejście do programowej nawigacji w najnowszej react-routerwersji v4
spik3s

56

Oto jak to zrobić za react-router v2.0.0pomocą ES6 . react-routerodszedł od mixinów.

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
Uważam, że obecną rekomendacją jest używanie historysingletonu, jak stwierdził @Bobby. Państwo mogli używać context.router, ale robisz to naprawdę trudne do badanej jednostki te elementy jako instancji tylko , że komponent nie będzie mieć to w kontekście.
George Mauer

2
@GeorgeMauer Myślę, że to zależy od tego, gdzie działa kod. Zgodnie z dokumentacją reagowania, użycie pliku context.router jest zalecane, jeśli jest uruchamiane na serwerze. github.com/reactjs/react-router/blob/master/upgrade-guides/…
KumarM

50

React-Router 4.x Odpowiedź:

Z mojej strony lubię mieć pojedynczy obiekt historii, który mogę przenosić nawet poza komponentami. Lubię robić jeden plik history.js, który importuję na żądanie i po prostu nim manipuluję.

Musisz tylko zmienić BrowserRouterna Router i podać propozycję historii. Nie zmienia to dla ciebie niczego poza tym, że masz swój własny obiekt historii, którym możesz manipulować, jak chcesz.

Musisz zainstalować historię , bibliotekę używaną przez react-router.

Przykładowe użycie, notacja 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>;
    }
}

EDYCJA 16 kwietnia 2018 r .:

Jeśli musisz nawigować z komponentu, który jest faktycznie renderowany z Routekomponentu, możesz również uzyskać dostęp do historii z rekwizytów, w ten sposób:

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
Z wyjątkiem dokumentów React Router, które mówią nam, że nie powinieneś, w większości przypadków, używać Routerzamiast BrowserRouter. BrowserRoutertworzy i utrzymuje historyobiekt dla Ciebie. Nie powinieneś domyślnie tworzyć własnych, tylko jeśli [„potrzebujesz głębokiej integracji z narzędziami do zarządzania stanem, takimi jak Redux.”] Reagtraining.com/react-router/web/api/Router
bobbyz

2
Tego nauczyłem się z czasem i doświadczeniem. Chociaż zwykle jest to dobra praktyka, aby używać wartości domyślnej this.props.history, nie znalazłem jeszcze rozwiązania, które mogłoby mi pomóc zrobić coś takiego w klasie, która nie jest komponentem, ani żadnym innym narzędziem, które nie zostało zbudowane jako komponent React, do którego możesz przekazywać rekwizyty. Dziękujemy za opinię :)
Eric Martin

44

Dla tego, który nie kontroluje strony serwera i dlatego korzysta z routera mieszającego v2:

Umieść swoją historię w osobnym pliku (np. App_history.js ES6):

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

export default appHistory;

I używaj go wszędzie!

Twój punkt wejścia do routera reagującego (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"]'));

Twoja nawigacja wewnątrz dowolnego komponentu (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
Dzięki. Może coś mi umknęło. Czy to nie jest dokładnie to samo, co dwie pierwsze odpowiedzi?
George Mauer,

2
Gdy używasz nawigacji „mieszającej”, w „React-routerze” v2 musisz zainstalować dodatkową „historię” modułu (jeśli chcesz uniknąć dziwnych zapytań mieszających), a zamiast korzystania z browserHistory, musisz użyć useRouterHistory (createHashHistory) ( {queryKey: false}). To jest główny powód, dla którego to opublikowałem. Pokazałem, jak nawigować za pomocą historii skrótów.
Aleksiej Wołodko

1
Och, rozumiem, masz rację. Nie jestem już przy tym projekcie, ale myślę, że preferowanym sposobem jest teraz zastosowanie historyobu tych metod
George Mauer

4
dlaczego ta odpowiedź nie jest najlepsza? dlaczego masz tyle kłopotów z kontekstem, HOC lub prototypami wydarzeń, dlaczego po prostu nie zaimportować historii i używać jej w dowolnym miejscu? próbuję zrozumieć kompromis
storm_buster

@storm_buster nie ma kompromisu. Mamy moduły ES6 i powinniśmy ich używać! Jest to typowa prezentacja użycia narzędzia (w tym przypadku zareaguj) do wszystkiego - nawet do rzeczy, do których tak naprawdę nie chcesz ich używać.
Capaj,

40

React Router V4

tl: dr;

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

Prosta i deklaratywna odpowiedź brzmi: musisz używać <Redirect to={URL} push={boolean} />w połączeniu zsetState()

push: boolean - gdy ma wartość true, przekierowanie spowoduje wypchnięcie nowego wpisu do historii zamiast zastępowania bieżącego.


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

Pełny przykład tutaj . Przeczytaj więcej tutaj .

PS. W przykładzie użyto inicjalizatorów właściwości ES7 + do inicjalizacji stanu. Zajrzyj tutaj , jeśli jesteś zainteresowany.


To najlepsza odpowiedź, jaką znalazłem. Używanie withRouternie działało, gdy znajduje się na przeładowywanej trasie. W naszym przypadku musieliśmy selektywnie zrobić setState(powodując return <Redirect>), jeśli już jesteś na trasie lub history.push()z innego miejsca.
karmakaze

Bardzo, bardzo sprytny. A jednak bardzo proste. Tks. Użyłem go w ReactRouterTable, ponieważ chciałem obsłużyć kliknięcie całej linii. Używane w onRowClick, aby ustawić stan. setState ({navigateTo: "/ path /" + row.uuid});
Lovato,

to jest odpowiedź kuloodporna.
Nitin Jadhav

31

Ostrzeżenie: ta odpowiedź dotyczy tylko wersji ReactRouter wcześniejszych niż 1.0

Zaktualizuję tę odpowiedź o przypadki użycia 1.0.0-rc1 później!

Możesz to zrobić również bez miksów.

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

Gotcha z kontekstami polega na tym, że nie jest dostępna, dopóki nie zdefiniujesz contextTypesklasy.

Jeśli chodzi o kontekst, jest to obiekt, podobnie jak rekwizyty, przekazywany z rodzica na dziecko, ale przekazywany jest w sposób dorozumiany, bez konieczności każdorazowego zmieniania rekwizytów. Zobacz https://www.tildedave.com/2014/11/15/introduction-to-contexts-in-react-js.html


26

Próbowałem co najmniej 10 sposobów na zrobienie tego, zanim coś zadziałało!

Odpowiedź @Felipe Skinner withRouterbyła dla mnie nieco przytłaczająca i nie byłem pewien, czy chcę utworzyć nowe nazwy klas „ExportedWithRouter”.

Oto najprostszy i najczystszy sposób, aby to zrobić, około obecnych React-Router 3.0.0 i ES6:

React-Router 3.xx z 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);

lub, jeśli nie jest to domyślna klasa, wyeksportuj:

withRouter(Example);
export { Example };

Zauważ, że w 3.xx <Link>sam komponent używa router.push, więc możesz przekazać mu wszystko, co przekażesz <Link to=tag, na przykład:

   this.props.router.push({pathname: '/some/path', query: {key1: 'val1', key2: 'val2'})'

1
Działa całkowicie dobrze. Ale odpowiada 200 OKzamiast 30xkodu. Jak mogę rozwiązać ten problem?
Muhammad Ateeq Azam

1
Niepewny. To nigdy nie przyszło mi do głowy, ponieważ nie jest to zewnętrzne żądanie - to tylko twoja przeglądarka używa własnego kodu strony, aby zmienić to, co jest na stronie. Zawsze możesz ręcznie przekierować javascript.
Ben Wheeler,

1
można go również używać z @withRouter jako dekoratorem klas ... świetnie pasuje do mobx
Dan

21

Aby nawigować programowo, musisz przesłać nową historię do props.history w swoim component, aby coś takiego mogło dla ciebie wykonać:

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

Proszę napisać wersję routera reagującego
Ash

19

W przypadku komponentów ES6 + React następujące rozwiązanie działało dla mnie.

Śledziłem Felippe Skinner, ale dodałem kompleksowe rozwiązanie, aby pomóc początkującym, takim jak ja.

Poniżej są używane wersje:

„reag-router”: „^ 2.7.0”

„reagować”: „^ 15.3.1”

Poniżej znajduje się mój komponent reagujący, w którym korzystałem z nawigacji programowej za pomocą routera reagującego:

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;

Poniżej znajduje się konfiguracja mojego routera:

 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'));

19

To może nie być najlepsze podejście, ale ... Używając React-Router v4, poniższy Typescript może dać pomysł na niektóre.

W wydanego składnika poniżej, np LoginPage, routerobiekt jest dostępny i po prostu zadzwoń router.transitionTo('/homepage')do nawigacji.

Kod nawigacji został pobrany z .

"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')
);


1
Dlaczego nie korzystasz z routera i przeglądarki?
Erwin Mayer

1
@Erwin Interfejs API jest inny w wersji 4. Zobacz to github.com/ReactTraining/react-router/issues/3847 .
mcku,

1
@mcku Skąd masz plik dts maszynopisu dla reagującego routera v4?
jmathew

1
@jmathew Nie mam żadnych definicji typu dla routera
reakcyjnego

16

W React-Router v4 i ES6

Możesz użyć withRouteri 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
Wspomniany już w kilku innych odpowiedziach (w tym obszernie w górnej odpowiedzi)
George Mauer,

13

Aby używać withRouterz komponentem opartym na klasach, spróbuj czegoś takiego poniżej. Nie zapomnij zmienić instrukcji eksportu, aby użyć 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

na podstawie poprzedniej odpowiedzi
José Antonio Postigo i Bena Wheelera
nowość? ma być napisany pismem maszynowym
i użyciem dekoratorów
LUB właściwości / pola statycznego

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

z jakimkolwiek zainstalowanym dzisiaj npm. „reaguj-router”: „^ 3.0.0” i
„@ typy / reaguj-router”: „^ 2.0.41”


11

z React-Router v4 na horyzoncie istnieje teraz nowy sposób na zrobienie tego.

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"/>

React-Lego to przykładowa aplikacja, która pokazuje, jak używać / aktualizować React-Router i zawiera przykładowe testy funkcjonalne, które poruszają się po aplikacji.


5
Jest to świetne do nawigacji z funkcji renderowania, chociaż zastanawiam się, jak nawigować z czegoś takiego jak hak cyklu życia lub redux?
Ruben Martinez Jr.,

11

W reakcji router v4. Podążam tą drogą w dwóch kierunkach, aby programować trasy.

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

Numer dwa

Zastępuje bieżący wpis na stosie historii

Aby uzyskać historię w rekwizytach, być może będziesz musiał owinąć swój komponent

withRouter


Dzięki!! Najlepsza odpowiedź
Taha Farooqui

9

Jeśli używasz historii skrótów lub przeglądarki, możesz to zrobić

hashHistory.push('/login');
browserHistory.push('/login');

2
hashHistory.push, nie można odczytać właściwości „push” niezdefiniowanej. Skąd je importujesz?
ArchNoob

importuj {hashHistory} z „reagującego routera”; możesz go zaimportować w ten sposób u góry.
Zaman Afzal,

Korzystam z twojego kodu próbki z mojego pliku saga redux, importuję tak jak powiedziałeś i używam go, ale wciąż otrzymuję ten błąd typu.
ArchNoob,

8

W obecnej wersji React (15.3) this.props.history.push('/location');działało dla mnie, ale wyświetlało następujące ostrzeżenie:

browser.js: 49 Ostrzeżenie: [reaktor-router] props.historyi context.historysą przestarzałe. Proszę używać context.router.

i rozwiązałem to w context.routernastępujący sposób:

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
Działa to dla V4. Cóż ... zamknij ... tak naprawdę musi to być this.context.router.history.push ('/ back-location');
velohomme

7

React-Router V4

jeśli używasz wersji 4, możesz użyć mojej biblioteki (wtyczka Shameless), w której po prostu wywołujesz akcję i wszystko po prostu działa!

dispatch(navigateTo("/aboutUs"));

trippler


5

Ci, którzy mają problemy z implementacją tego na React-Router v4.

Oto działające rozwiązanie do poruszania się po aplikacji React od działań redux.

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>

inny_plik.js LUB plik redux

import history from './history' 

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

Wszystko dzięki temu komentarzowi: komentarz dotyczący problemów ReactTraining


4

Jeśli zdarzy się sparowanie RR4 w / redux poprzez React -Router-Redux , skorzystaj z opcji tworzenia akcji routingu od react-router-reduxrównież.

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)

Jeśli używasz redux thunk / saga do zarządzania przepływem asynchronicznym, zaimportuj powyższych twórców akcji w działaniach redux i podpnij się, aby zareagować na komponenty za pomocą mapDispatchToProps.


1
react-router-reduxbył przestarzały / archiwizowany przez długi czas
oligofren

4

Możesz także użyć useHistoryhaka w komponencie bezstanowym. Przykład z dokumentów.

import { useHistory } from "react-router"

function HomeButton() {
  const history = useHistory()

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

Uwaga: Haki zostały dodane react-router@5.1.0i wymagająreact@>=16.8


1
Dobra rozmowa, czy mógłbyś zauważyć, która wersja Reagera-Routera i reakcja, której dotyczy? To nowa zmiana, która nie zawsze była dostępna
George Mauer

3

Właściwa odpowiedź była dla mnie w momencie pisania

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

Ale musisz dodać PropTypes do swojego komponentu

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

Nie zapomnij zaimportować PropTypes

import PropTypes from 'prop-types';

Napisz wersję routera reagującego i co zaimportowałeś z routera reagującego
Ash

Nie ma nic do zaimportowania, a to jest wersja „Reaktywuj router”: „^ 4.2.0”
webmaster

3

Może nie jest to najlepsze rozwiązanie, ale wykonuje zadanie:

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

Zasadniczo logika powiązana z jedną akcją (w tym przypadku po usunięciu) spowoduje wywołanie wyzwalacza przekierowania. Nie jest to idealne, ponieważ dodasz „wyzwalacz” węzła DOM do swojego znacznika, aby móc wygodnie wywoływać go w razie potrzeby. Będziesz także bezpośrednio wchodził w interakcje z DOM, co w komponencie React może nie być pożądane.

Jednak ten typ przekierowań nie jest tak często wymagany. Tak więc jedno lub dwa dodatkowe, ukryte łącza w znacznikach komponentów nie zaszkodziłyby tak bardzo, szczególnie jeśli nadasz im sensowne nazwy.


Czy możesz wyjaśnić, dlaczego kiedykolwiek używałbyś tego w stosunku do przyjętego rozwiązania?
George Mauer,

To tylko alternatywne rozwiązanie omawianego problemu. Jak już wspomniano, nie jest idealny, ale działa dobrze w przypadku przekierowań programowych.
neatsu,

2

To działało dla mnie, nie potrzebowałem specjalnego importu:

<input 
  type="button" 
  name="back" 
  id="back" 
  class="btn btn-primary" 
  value="Back" 
  onClick={() => { this.props.history.goBack() }} 
/>

Oczywiście wymaga to specjalnego importu - komponent nie został historydodany propssam. Pochodzi z HoC, który musisz zaimportować, aby użyć (komponenty wysłane bezpośrednio przez Routesą automatycznie pakowane przez ten HoC, ale potem potrzebujesz importu dla Route...)
George Mauer

2

zamiast tego użyj hookroutera, nowoczesnej alternatywy dla routera reagującego

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

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

aby odpowiedzieć na pytanie OP dotyczące łączenia poprzez pole wyboru, możesz to zrobić:

navigate('/about');

Do Twojej wiadomości Sam autor nazywa tę bibliotekę „dowodem koncepcji” i od prawie roku nie ma żadnej aktywności (czerwiec 2019 r.).
UWAGI

@ Uwaga, ponieważ jest już idealna
StefanBob

Cóż, 38 otwartych problemów, wraz z faktem, że wersja 2.0 będzie kompletnym przepisywaniem według autora, mówi inaczej.
UWAGI

1

Dla React Router v4 +

Zakładając, że nie będziesz musiał nawigować podczas samego renderowania (do którego możesz użyć <Redirect>komponentu), to właśnie robimy w naszej aplikacji.

Zdefiniuj pustą trasę, która zwraca null, pozwoli to uzyskać dostęp do obiektu historii. Musisz to zrobić na najwyższym poziomie, na którym Routerjest zdefiniowany.

Teraz można zrobić wszystkie rzeczy, które można zrobić na historii jak history.push(), history.replace(), history.go(-1)itp!

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

React-router-dom: 5.1.2

  • Ta strona ma 3 strony, z których wszystkie są renderowane dynamicznie w przeglądarce.

  • Chociaż strona nigdy się nie odświeża, zauważ, jak React Router aktualizuje adres URL podczas przeglądania witryny. Pozwala to zachować historię przeglądarki, upewniając się, że przycisk Wstecz i zakładki działają poprawnie

  • Przełącznik przegląda wszystkich swoich dzieci i elementów sprawia, że pierwszy z nich, którego ścieżka odpowiada aktualny adres URL. Korzystaj z niego, gdy masz wiele tras, ale chcesz, aby renderowała tylko jedna z nich na raz

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

Witamy w SO! Kiedy odpowiadasz na pytanie z tak wieloma odpowiedziami, spróbuj pokazać swoje zalety.
David García Bodego

To również nie odpowiada na pytanie, jak to zrobić programowo z imperative js, a nie ze znacznikami.
George Mauer,

1

Tak więc w mojej odpowiedzi istnieją 3 różne sposoby programowego przekierowania na trasę. Niektóre rozwiązania zostały już zaprezentowane, ale następne koncentrują się wyłącznie na elementach funkcjonalnych z dodatkową aplikacją demonstracyjną.

Korzystanie z następujących wersji:

zareagować: 16.13.1

Reaguj : 16.13.1

Reaguj router: 5.2.0

React-router-dom: 5.2.0

maszynopis: 3.7.2

Konfiguracja:

Przede wszystkim rozwiązanie używa HashRouter, skonfigurowane w następujący sposób:

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

Z dokumentacji dotyczącej <HashRouter>:

A, <Router>który używa części skrótu adresu URL (tj. window.location.hash), Aby utrzymać interfejs użytkownika w synchronizacji z adresem URL.

Rozwiązania:

  1. Za <Redirect>pomocą push za pomocą useState:

Używając funkcjonalnego komponentu ( RedirectPushActionkomponentu z mojego repozytorium) możemy użyć useStatedo obsługi przekierowania. Trudna część polega na tym, że po przekierowaniu musimy redirectprzywrócić stan false. Korzystając setTimeOutz 0opóźnieniem, czekamy na potwierdzenie ReactRedirect przejdzie do DOM, a następnie zwróci przycisk, aby użyć go następnym razem.

Mój przykład poniżej:

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>
</>

Z <Redirect>dokumentacji:

Renderowanie a <Redirect>spowoduje przejście do nowej lokalizacji. Nowa lokalizacja zastąpi bieżącą lokalizację na stosie historii, podobnie jak przekierowania po stronie serwera (HTTP 3xx).

  1. Za pomocą useHistoryhaka:

W moim rozwiązaniu istnieje komponent o nazwie, UseHistoryActionktóry reprezentuje następujące elementy:

let history = useHistory();

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

useHistoryHak daje nam dostęp do obiektu historii, która pomaga nam programowo nawigować lub zmiany trasy.

  1. Za pomocą withRouteruzyskaj historyod props:

Utworzono jeden komponent o nazwie WithRouterAction, wyświetla się jak poniżej:

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

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

export default withRouter(WithRouterAction);

Czytanie z withRouterdokumentacji:

Możesz uzyskać dostęp do historywłaściwości obiektu i najbliższego <Route>dopasowania za pomocą withRouterkomponentu wyższego rzędu. withRouterprzejdzie aktualizowany match, locationi historypodpory zawiniętego składnika gdy to powoduje.

Próbny:

Dla lepszej reprezentacji zbudowałem repozytorium GitHub z tymi przykładami, znajdź go poniżej:

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

Mam nadzieję, że to pomoże!

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.