React-router: Jak ręcznie wywołać Link?

131

Jestem nowy w ReactJS i React-Router. Mam komponent, który odbiera przez props <Link/>obiekt z routera reagującego . Za każdym razem, gdy użytkownik kliknie przycisk „Dalej” w tym komponencie, chcę <Link/>ręcznie wywołać obiekt.

W tej chwili używam referencji, aby uzyskać dostęp do instancji zapasowej i ręcznie klikam <Link/>generowany tag „a” .

Pytanie: Czy istnieje sposób na ręczne wywołanie łącza (np. this.props.next.go)?

Oto obecny kod, który mam:

//in MasterPage.js
var sampleLink = <Link to="/sample">Go To Sample</Link>
<Document next={sampleLink} />

//in Document.js
...
var Document = React.createClass({
   _onClickNext: function() {
      var next = this.refs.next.getDOMNode();
      next.querySelectorAll('a').item(0).click(); //this sounds like hack to me
   },
   render: function() {
      return (
         ...
         <div ref="next">{this.props.next} <img src="rightArrow.png" onClick={this._onClickNext}/></div>
         ...
      );
   }
});
...

Oto kod, który chciałbym mieć:

//in MasterPage.js
var sampleLink = <Link to="/sample">Go To Sample</Link>
<Document next={sampleLink} />

//in Document.js
...
var Document = React.createClass({
   render: function() {
      return (
         ...
         <div onClick={this.props.next.go}>{this.props.next.label} <img src="rightArrow.png" /> </div>
         ...
      );
   }
});
...
Alan Souza
źródło

Odpowiedzi:

199

React Router v4 - składnik przekierowania (zaktualizowany 2017/04/15)

Zalecanym sposobem v4 jest umożliwienie metodzie renderowania wychwycenia przekierowania. Użyj state lub props, aby określić, czy komponent przekierowania musi być pokazany (który następnie wyzwala przekierowanie).

import { Redirect } from 'react-router';

// ... your class implementation

handleOnClick = () => {
  // some action...
  // then redirect
  this.setState({redirect: true});
}

render() {
  if (this.state.redirect) {
    return <Redirect push to="/sample" />;
  }

  return <button onClick={this.handleOnClick} type="button">Button</button>;
}

Źródła: https://reacttraining.com/react-router/web/api/Redirect

React Router v4 - odniesienie do kontekstu routera

Możesz także skorzystać z Routerkontekstu, który jest widoczny dla składnika React.

static contextTypes = {
  router: PropTypes.shape({
    history: PropTypes.shape({
      push: PropTypes.func.isRequired,
      replace: PropTypes.func.isRequired
    }).isRequired,
    staticContext: PropTypes.object
  }).isRequired
};

handleOnClick = () => {
  this.context.router.push('/sample');
}

Tak <Redirect />działa pod maską.

Źródła: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Redirect.js#L46,L60

React Router v4 - Zewnętrznie mutujący obiekt historii

Jeśli nadal potrzebujesz zrobić coś podobnego do implementacji v2, możesz utworzyć kopię, BrowserRoutera następnie historywyeksportować stałą. Poniżej znajduje się podstawowy przykład, ale w razie potrzeby możesz go skomponować, aby wprowadzić do niego konfigurowalne rekwizyty. Istnieją pewne zastrzeżenia dotyczące cykli życia, ale router powinien zawsze podlegać ponownej wersji routera, tak jak w wersji 2. Może to być przydatne w przypadku przekierowań po żądaniu interfejsu API z funkcji akcji.

// browser router file...
import createHistory from 'history/createBrowserHistory';
import { Router } from 'react-router';

export const history = createHistory();

export default class BrowserRouter extends Component {
  render() {
    return <Router history={history} children={this.props.children} />
  }
}

// your main file...
import BrowserRouter from './relative/path/to/BrowserRouter';
import { render } from 'react-dom';

render(
  <BrowserRouter>
    <App/>
  </BrowserRouter>
);

// some file... where you don't have React instance references
import { history } from './relative/path/to/BrowserRouter';

history.push('/sample');

Najnowsze BrowserRouterdo rozszerzenia: https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/BrowserRouter.js

React Router v2

Przekaż nowy stan do browserHistoryinstancji:

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

Źródła: https://github.com/reactjs/react-router/blob/master/docs/guides/NavigatingOutsideOfComponents.md

Matt Lo
źródło
7
hashHistory.push ('/ sample'); jeśli używasz hashHistory zamiast browserHistory
sanath_p
1
jest to szczególnie przydatne w bibliotece material-ui, ponieważ użycie containerElement = {<Link to = "/" />} nie zawsze wywołuje link
Vishal Disawar
2
Zauważ, że w przypadku opcji przekierowania musisz określić push (tj. <Redirect push />). Domyślnie dokona zamiany, która wcale nie jest tym samym, co ręczne wywołanie linku
aw04
1
@jokab możesz użyć <NavLink /> zamiast <Link /> github.com/ReactTraining/react-router/blob/master/packages/…
Matt Lo
1
Przekierowanie nie działa dla mnie, ale rozwiązanie AW04 z withRouter jest prostsze i działa
stackdave
90

React Router 4 zawiera withRouter HOC, który daje dostęp do historyobiektu poprzez this.props:

import React, {Component} from 'react'
import {withRouter} from 'react-router-dom'

class Foo extends Component {
  constructor(props) {
    super(props)

    this.goHome = this.goHome.bind(this)
  }

  goHome() {
    this.props.history.push('/')
  }

  render() {
    <div className="foo">
      <button onClick={this.goHome} />
    </div>
  }
}

export default withRouter(Foo)
aw04
źródło
9
U mnie to zadziałało i wygląda na najprostsze rozwiązanie.
Rubycut
5
To najlepsze rozwiązanie. Nie rozumiem, dlaczego ma tak mało głosów.
Benoit,
1
tak, możesz kliknąć link kilka razy, a powrót przeglądarki nie zadziała. musisz kilka razy kliknąć wstecz w przeglądarce, aby naprawdę wrócić
Vladyslav Tereshyn
@VladyslavTereshyn, możesz dodać logikę warunkową: if ((this.props.location.pathname + this.props.location.search)! == navigateToPath) {...}
MattWeiler
15

W wersji 5.x możesz użyć useHistoryhooka react-router-dom:

// Sample extracted from https://reacttraining.com/react-router/core/api/Hooks/usehistory
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>
  );
}
Paulo Mateus
źródło
To najlepsze rozwiązanie. Jeśli dodasz logikę warunkową, możesz uniknąć zduplikowanych wpisów w historii, gdy użytkownik kliknie ten sam przycisk wiele razy:if ((routerHistory.location.pathname + routerHistory.location.search) !== navigateToPath) { routerHistory.push(navigateToPath); }
MattWeiler
Musiałem zadeklarować historyzmienną, wywoływanie katalogu useHistory().pushnie jest dozwolone przez reguły hooków
onmyway133
wydaje się to najnowocześniejszym rozwiązaniem.
Youngjae
8

https://github.com/rackt/react-router/blob/bf89168acb30b6dc9b0244360bcbac5081cf6b38/examples/transitions/app.js#L50

lub możesz nawet spróbować wykonać onClick to (bardziej brutalne rozwiązanie):

window.location.assign("/sample");
grechut
źródło
Gdy zmieniają się wiersze kodu, twoja odpowiedź będzie lepsza, jeśli skopiujesz szczegóły i wyjaśnisz swoją odpowiedź tutaj. Ponadto assignnie jest własnością, to funkcja.
WiredPrairie
(Ale nadal masz tylko łącze do określonej linii pliku). W odpowiedzi podaj konkretną sugestię, a nie tylko link.
WiredPrairie
Dzięki za odpowiedź @grechut. Jednak chcę się upewnić, że Document w ogóle nie wie nic o routerze. Spodziewane zachowanie to: „Jeśli użytkownik kliknie strzałkę w prawo, wywołaj następną funkcję”. Następną funkcją może być łącze lub nie.
Alan Souza,
Mam kilka stron obsługiwanych poza Reactem (ekrany logowania z przekierowaniami FB i Google), więc potrzebowałem tego w nawigacji dla tych stron, ponieważ "browserHistory.push ('/ home');" zmienił tylko adres URL, nie był w stanie przekierować stron. Dziękuję Ci.
Deborah
7
Spowoduje to ponowne załadowanie strony @grechut, co nie jest pożądanym zachowaniem dla aplikacji z routerem reagującym.
Abhas
2

Ok, myślę, że udało mi się znaleźć na to odpowiednie rozwiązanie.

Teraz, zamiast wysyłać <Link/>jako prop do Documenta, wysyłam, <NextLink/>który jest niestandardowym opakowaniem dla Linkreact-router. W ten sposób mogę ustawić strzałkę w prawo jako część struktury Link, jednocześnie unikając umieszczania kodu routingu w obiekcie Document.

Zaktualizowany kod wygląda następująco:

//in NextLink.js
var React = require('react');
var Right = require('./Right');

var NextLink = React.createClass({
    propTypes: {
        link: React.PropTypes.node.isRequired
    },

    contextTypes: {
        transitionTo: React.PropTypes.func.isRequired
    },

    _onClickRight: function() {
        this.context.transitionTo(this.props.link.props.to);
    },

    render: function() {
        return (
            <div>
                {this.props.link}
                <Right onClick={this._onClickRight} />
            </div>  
        );
    }
});

module.exports = NextLink;

...
//in MasterPage.js
var sampleLink = <Link to="/sample">Go To Sample</Link>
var nextLink = <NextLink link={sampleLink} />
<Document next={nextLink} />

//in Document.js
...
var Document = React.createClass({
   render: function() {
      return (
         ...
         <div>{this.props.next}</div>
         ...
      );
   }
});
...

PS : Jeśli używasz najnowszej wersji React-router, może być konieczne użycie this.context.router.transitionTozamiast this.context.transitionTo. Ten kod będzie działał dobrze dla routera React w wersji 0.12.X.

Alan Souza
źródło
2

React Router 4

Możesz łatwo wywołać metodę push za pośrednictwem kontekstu w wersji 4:

this.context.router.push(this.props.exitPath);

gdzie kontekst to:

static contextTypes = {
    router: React.PropTypes.object,
};
Chris
źródło
Używając BrowserRouter, obiekt kontekstu moich komponentów nie zawiera routerobiektu. Czy robię coś złego?
pilau
Czy ustawiasz kontekst w komponencie (drugi blok powyżej)?
Chris
Dzięki za bicie! ostatecznie to pracował dla mnie router: React.PropTypes.object.isRequired. Nie wiem, dlaczego nie zadziałało bez isRequiredklucza. <Link>Wydaje się również, że jestem w stanie uzyskać historykontekst, ale nie mogłem go odtworzyć.
pilau
Interesujące - jeśli umieścisz kodepen, pomogę ci go zdebugować, jeśli nadal utkniesz
Chris
Wygląda na to, że możesz używać this.props.history.push()w React Router v4. Znalazłem to tylko przez sprawdzenie rekwizytów, które przechodzi przez React Router. Wydaje się, że działa, ale nie jestem pewien, czy to dobry pomysł.
sean_j_roberts
-1

znowu to jest JS :) to nadal działa ....

var linkToClick = document.getElementById('something');
linkToClick.click();

<Link id="something" to={/somewhaere}> the link </Link>
taggartJ
źródło