it-swarm.com.ru

Как перейти к истории в React Router v4?

В текущей версии React Router (v3) я могу принять ответ сервера и использовать browserHistory.Push, чтобы перейти на соответствующую страницу ответа. Тем не менее, это не доступно в v4, и я не уверен, как правильно это сделать.

В этом примере, используя Redux, components/app-product-form.js вызывает this.props.addProduct(props), когда пользователь отправляет форму. Когда сервер возвращает успех, пользователь попадает на страницу корзины.

// actions/index.js
export function addProduct(props) {
  return dispatch =>
    axios.post(`${ROOT_URL}/cart`, props, config)
      .then(response => {
        dispatch({ type: types.AUTH_USER });
        localStorage.setItem('token', response.data.token);
        browserHistory.Push('/cart'); // no longer in React Router V4
      });
}

Как сделать перенаправление на страницу корзины из функции для React Router v4?

252
Chris

Вы можете использовать методы history вне ваших компонентов. Попробуйте следующим образом.

Сначала создайте объект history used пакет истории :

// src/history.js

import { createBrowserHistory } from 'history';

export default createBrowserHistory();

Затем оберните его в <Router> (обратите внимание, вы должны использовать import { Router } вместо import { BrowserRouter as Router }):

// src/index.jsx

// ...
import { Router, Route, Link } from 'react-router-dom';
import history from './history';

ReactDOM.render(
  <Provider store={store}>
    <Router history={history}>
      <div>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to="/login">Login</Link></li>
        </ul>
        <Route exact path="/" component={HomePage} />
        <Route path="/login" component={LoginPage} />
      </div>
    </Router>
  </Provider>,
  document.getElementById('root'),
);

Измените свое текущее местоположение из любого места, например:

// src/actions/userActionCreators.js

// ...
import history from '../history';

export function login(credentials) {
  return function (dispatch) {
    return loginRemotely(credentials)
      .then((response) => {
        // ...
        history.Push('/');
      });
  };
}

UPD: Вы также можете увидеть немного другой пример в React Router FAQ .

143
Oleg Belostotsky

React Router v4 принципиально отличается от v3 (и более ранних версий), и вы не можете выполнять browserHistory.Push(), как раньше.

Это обсуждение кажется связанным, если вы хотите больше информации:

  • Создание нового browserHistory не будет работать, потому что <BrowserRouter> создает свой собственный экземпляр истории и прислушивается к изменениям. Таким образом, другой экземпляр изменит URL, но не обновит <BrowserRouter>.
  • browserHistory не выставляется реагирующим маршрутизатором в v4, только в v2.

Вместо этого у вас есть несколько вариантов сделать это:

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

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

    import React from "react";
    import { withRouter } from "react-router-dom";
    
    class MyComponent extends React.Component {
      ...
      myFunction() {
        this.props.history.Push("/some/Path");
      }
      ...
    }
    export default withRouter(MyComponent);
    

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

    Вы можете получить доступ к свойствам объекта history и ближайших <Route> 's match через компонент высшего порядка withRouter. withRouter будет перерисовывать свой компонент каждый раз, когда маршрут меняется с теми же реквизитами, что и <Route> render props: { match, location, history }.


  • Используйте API context

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

    import React from "react";
    import PropTypes from "prop-types";
    
    class MyComponent extends React.Component {
      static contextTypes = {
        router: PropTypes.object
      }
      constructor(props, context) {
         super(props, context);
      }
      ...
      myFunction() {
        this.context.router.history.Push("/some/Path");
      }
      ...
    }
    

    Взгляните на официальную документацию в контексте:

    Если вы хотите, чтобы ваше приложение было стабильным, не используйте контекст. Это экспериментальный API, и он может сломаться в будущих версиях React.

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

252
Chris

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

import React, {Component} from 'react';

export default class Link extends Component {
    constructor(props) {
        super(props);
        this.onLogout = this.onLogout.bind(this);
    }
    onLogout() {
        this.props.history.Push('/');
    }
    render() {
        return (
            <div>
                <h1>Your Links</h1>
                <button onClick={this.onLogout}>Logout</button>
            </div>
        );
    }
}

Используйте this.props.history.Push('/cart'); для перенаправления на страницу корзины, она будет сохранена в объекте истории.

Наслаждайся, Майкл.

21
Michael Horojanski

Согласно Документация по React Router v4 - Сессия Redux Deep Integration

Глубокая интеграция необходима для: 

«уметь ориентироваться, отправляя действия»

Однако они рекомендуют этот подход в качестве альтернативы "глубокой интеграции":

«Вместо того, чтобы отправлять действия для навигации, вы можете передать предоставленный объект истории, чтобы направить компоненты к вашим действиям и перемещаться с ним там».

Таким образом, вы можете обернуть ваш компонент компонентом высшего порядка withRouter:

export default withRouter(connect(null, { actionCreatorName })(ReactComponent));

который будет передавать историю API для реквизита. Таким образом, вы можете назвать создателя действия, передавая историю в качестве параметра. Например, внутри вашего ReactComponent:

onClick={() => {
  this.props.actionCreatorName(
    this.props.history,
    otherParams
  );
}}

Затем внутри ваших действий/index.js:

export function actionCreatorName(history, param) {
  return dispatch => {
    dispatch({
      type: SOME_ACTION,
      payload: param.data
    });
    history.Push("/path");
  };
}
17
alainbex

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

Оберните ваш контейнер withRouter и передайте историю вашим действиям в функции mapDispatchToProps. В действии используйте history.Push ('/ url') для навигации. 

Действие:

export function saveData(history, data) {
  fetch.post('/save', data)
     .then((response) => {
       ...
       history.Push('/url');
     })
};

Контейнер: 

import { withRouter } from 'react-router-dom';
...
const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    save: (data) => dispatch(saveData(ownProps.history, data))}
};
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Container));

Это действительно для React Router v4.x .

15
Khrystyna Skvarok

this.context.history.Push не будет работать.

Мне удалось заставить Push работать так:

static contextTypes = {
    router: PropTypes.object
}

handleSubmit(e) {
    e.preventDefault();

    if (this.props.auth.success) {
        this.context.router.history.Push("/some/Path")
    }

}
6
Aimar

В этом случае вы передаете реквизит своему толпу. Так что вы можете просто позвонить

props.history.Push('/cart')

Если это не так, вы все равно можете передать историю из вашего компонента 

export function addProduct(data, history) {
  return dispatch => {
    axios.post('/url', data).then((response) => {
      dispatch({ type: types.AUTH_USER })
      history.Push('/cart')
    })
  }
}
5
user3812377

Простейшим способом в React Router 4 является использование 

this.props.history.Push('/new/url');

Но чтобы использовать этот метод, ваш существующий компонент должен иметь доступ к объекту history. Мы можем получить доступ по

  1. Если ваш компонент связан с Route напрямую, то ваш компонент уже имеет доступ к объекту history

    например:

    <Route path="/profile" component={ViewProfile}/>
    

    Здесь ViewProfile имеет доступ к history.

  2. Если не подключен к Route напрямую.

    например:

    <Route path="/users" render={() => <ViewUsers/>}
    

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

    Внутри ViewUsers компонент 

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

    • export default withRouter(ViewUsers);

    Вот и все, ваш компонент ViewUsers имеет доступ к объекту history.

ОБНОВЛЕНИЕ

2- в этом сценарии передайте весь маршрут props вашему компоненту, и тогда мы сможем получить доступ к this.props.history из компонента даже без HOC

например:

<Route path="/users" render={props => <ViewUsers {...props} />}
5
Saahithyan Vigneswaran

Я предлагаю еще одно решение, если оно того стоит.

У меня есть файл history.js, где у меня есть следующее:

import createHistory from 'history/createBrowserHistory'
const history = createHistory()
history.pushLater = (...args) => setImmediate(() => history.Push(...args))
export default history

Далее, в моем Root, где я определяю свой маршрутизатор, я использую следующее:

import history from '../history'
import { Provider } from 'react-redux'
import { Router, Route, Switch } from 'react-router-dom'

export default class Root extends React.Component {
  render() {
    return (
     <Provider store={store}>
      <Router history={history}>
       <Switch>
        ...
       </Switch>
      </Router>
     </Provider>
    )
   }
  }

Наконец, на своем actions.js я импортирую историю и использую pushLater

import history from './history'
export const login = createAction(
...
history.pushLater({ pathname: PATH_REDIRECT_LOGIN })
...)

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

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

4
Diego

Используйте Обратный звонок. Это сработало для меня!

export function addProduct(props, callback) {
  return dispatch =>
    axios.post(`${ROOT_URL}/cart`, props, config)
    .then(response => {
    dispatch({ type: types.AUTH_USER });
    localStorage.setItem('token', response.data.token);
    callback();
  });
}

В компоненте вам просто нужно добавить обратный вызов

this.props.addProduct(props, () => this.props.history.Push('/cart'))
3
Priyansh Rastogi

Если вы используете Redux, я бы порекомендовал использовать пакет npm Reaction-router-redux . Позволяет отправлять в Redux магазин навигационных действий.

Вы должны создать хранилище, как описано в их файле Readme .

Самый простой вариант использования:

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

this.props.dispatch(Push('/second page'));

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

Контейнер:

import { connect } from 'react-redux';
import { Push } from 'react-router-redux';

import Form from '../components/Form';

const mapDispatchToProps = dispatch => ({
  changeUrl: url => dispatch(Push(url)),
});

export default connect(null, mapDispatchToProps)(Form);

Составная часть:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

export default class Form extends Component {
  handleClick = () => {
    this.props.changeUrl('/secondPage');
  };

  render() {
    return (
      <div>
        <button onClick={this.handleClick}/>
      </div>Readme file
    );
  }
}
2
Black

Я смог сделать это с помощью bind(). Я хотел нажать кнопку в index.jsx, опубликовать некоторые данные на сервере, оценить ответ и перенаправить на success.jsx. Вот как я это выяснил ...

index.jsx:

import React, { Component } from "react"
import { postData } from "../../scripts/request"

class Main extends Component {
    constructor(props) {
        super(props)
        this.handleClick = this.handleClick.bind(this)
        this.postData = postData.bind(this)
    }

    handleClick() {
        const data = {
            "first_name": "Test",
            "last_name": "Guy",
            "email": "[email protected]"
        }

        this.postData("person", data)
    }

    render() {
        return (
            <div className="Main">
                <button onClick={this.handleClick}>Test Post</button>
            </div>
        )
    }
}

export default Main

request.js:

import { post } from "./fetch"

export const postData = function(url, data) {
    // post is a fetch() in another script...
    post(url, data)
        .then((result) => {
            if (result.status === "ok") {
                this.props.history.Push("/success")
            }
        })
}

success.jsx:

import React from "react"

const Success = () => {
    return (
        <div className="Success">
            Hey cool, got it.
        </div>
    )
}

export default Success

Таким образом, связав this с postData в index.jsx, я смог получить доступ к this.props.history в request.js... затем я могу повторно использовать эту функцию в различных компонентах, просто убедитесь, что я не забыл включить this.postData = postData.bind(this) в constructor().

2
skwidbreth

React router V4 теперь позволяет использовать историю, как показано ниже:

this.props.history.Push("/dummy",value)

В этом случае к значению можно получить доступ везде, где есть свойство местоположения, как state:{value}, а не состояние компонента.

1
Snivio

Вот мой хак (это мой файл корневого уровня, с небольшим количеством избыточного кода, хотя я не использую react-router-redux):

const store = configureStore()
const customHistory = createBrowserHistory({
  basename: config.urlBasename || ''
})

ReactDOM.render(
  <Provider store={store}>
    <Router history={customHistory}>
      <Route component={({history}) => {
        window.appHistory = history
        return (
          <App />
        )
      }}/>
    </Router>
  </Provider>,
  document.getElementById('root')
)

Затем я могу использовать window.appHistory.Push() в любом месте, где захочу (например, в своих функциях/редукциях/sagas и т.д. в хранилище редуксов), я надеялся, что смогу просто использовать window.customHistory.Push(), но по какой-то причине react-router, похоже, никогда не обновлялся, даже если URL-адрес изменился. Но так у меня есть экземпляр EXACT, который использует react-router. Я не люблю помещать вещи в глобальную сферу, и это одна из немногих вещей, с которыми я бы сделал это. Но это лучше, чем любая другая альтернатива, которую я видел в ИМО.

1
Joao
/*Step 1*/
myFunction(){  this.props.history.Push("/home"); }
/**/
 <button onClick={()=>this.myFunction()} className={'btn btn-primary'}>Go 
 Home</button>
0
David Bagdasaryan

вы можете использовать это так, как я делаю это для входа в систему и разные вещи Мэнни 

class Login extends Component {
  constructor(props){
    super(props);
    this.login=this.login.bind(this)
  }


  login(){
this.props.history.Push('/dashboard');
  }


render() {

    return (

   <div>
    <button onClick={this.login}>login</login>
    </div>

)
0
jadlmir

так что я делаю это так: - вместо перенаправления с использованием history.Push, я просто использую компонент Redirect из react-router-dom При использовании этого компонента вы можете просто передать Push=true, и он позаботится обо всем остальном

import * as React from 'react';
import { Redirect } from 'react-router-dom';
class Example extends React.Component {
  componentDidMount() {
    this.setState({
      redirectTo: '/test/path'
    });
  }

  render() {
    const { redirectTo } = this.state;

    return <Redirect to={{pathname: redirectTo}} Push={true}/>
  }
}
0
LuizAsFight

шаг первый обернуть ваше приложение в маршрутизаторе

import { BrowserRouter as Router } from "react-router-dom";
ReactDOM.render(<Router><App /></Router>, document.getElementById('root'));

Теперь все мое приложение будет иметь доступ к BrowserRouter. Шаг второй: импортирую Route, а затем передаю эти реквизиты. Вероятно, в одном из ваших основных файлов. 

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

//lots of code here

//somewhere in my render function

    <Route
      exact
      path="/" //put what your file path is here
      render={props => (
      <div>
        <NameOfComponent
          {...props} //this will pass down your match, history, location objects
        />
      </div>
      )}
    />

Теперь, если я запускаю console.log (this.props) в моем файле js компонента, я должен получить что-то похожее на это

{match: {…}, location: {…}, history: {…}, //other stuff }

Шаг 2 Я могу получить доступ к объекту истории, чтобы изменить свое местоположение

//lots of code here relating to my whatever request I just ran delete, put so on

this.props.history.Push("/") // then put in whatever url you want to go to

Кроме того, я просто студент-программист, поэтому я не эксперт, но я знаю, что вы также можете использовать 

window.location = "/" //wherever you want to go

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

0
user2785628