Как нажать на историю в React Router v4?
в текущей версии React Router (v3) я могу принять ответ сервера и использовать browserHistory.push перейти на соответствующую страницу ответов. Однако это недоступно в v4, и я не уверен, что это подходящий способ справиться с этим.
в этом примере, используя Redux,компоненты / приложение-форма продукта.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?
15 ответов
можно использовать history методы внешней компоненты. Попробуйте следующим образом.
во-первых, создать history объект пакета истории:
// 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 маршрутизатор FAQ.
React Router v4 принципиально отличается от v3 (и ранее), и вы не можете сделать browserHistory.push() Как вы привыкли.
эта дискуссия кажется, если вы хотите получить больше информации:
- создание нового
browserHistoryне будет работать, потому что<BrowserRouter>создает свой собственный экземпляр истории и прослушивает изменения на этом. Таким образом, другой экземпляр изменит url-адрес, но не обновит<BrowserRouter>.
browserHistoryне предоставляется react-маршрутизатор в 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>' smatchчерез компонент высшего порядка withRouter. withRouter будет повторно отображать свой компонент каждый раз, когда маршрут изменяется с тем же реквизитом, что и<Route>выносят реквизит:{ match, location, history }.
- 
использоватьcontextAPIиспользование контекста может быть одним из самые простые решения, но будучи экспериментальным 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. 
вот как я это сделал:
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'); для перенаправления на страницу корзины он будет сохранен в объекте истории.
Наслаждайтесь, Майкл.
По данным React Router V4 документация-Redux глубокий сеанс интеграции
глубокая интеграция необходима для:
"уметь ориентироваться по диспетчерским действиям"
однако они рекомендуют этот подход в качестве альтернативы "глубокой интеграции":
" вместо диспетчеризации действий для навигации вы можете передать объект истории, предоставленный для маршрутизации компонентов к вашим действиям и навигации с ним там."
таким образом, вы можете обернуть свой компонент компонентом высокого порядка withRouter:
export default withRouter(connect(null, { actionCreatorName })(ReactComponent));
который передаст API истории реквизиту. Таким образом, вы можете назвать создателя действия, передающего историю как param. Например, внутри вашего ReactComponent:
onClick={() => {
  this.props.actionCreatorName(
    this.props.history,
    otherParams
  );
}}
затем, внутри ваших действий / индекса.js:
export function actionCreatorName(history, param) {
  return dispatch => {
    dispatch({
      type: SOME_ACTION,
      payload: param.data
    });
    history.push("/path");
  };
}
неприятный вопрос, занял у меня довольно много времени, но в конце концов я решил его так:
оберните контейнер с withRouter и передайте историю своему действию в . В действии используйте историю.нажмите ('/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));
это действительно для реагирует маршрутизатор v4.x.
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")
    }
}
в этом случае вы передаете реквизит вашему thunk. Поэтому вы можете просто позвонить
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')
    })
  }
}
Я предлагаю еще одно решение в случае, если это ценно для кого-то другого.
у меня есть history.js файл, где у меня есть следующее:
import createHistory from 'history/createBrowserHistory'
const history = createHistory()
history.pushLater = (...args) => setImmediate(() => history.push(...args))
export default history
далее, в моем корне, где я определяю свой маршрутизатор, я использую следующее:
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.
надеюсь, что это помогает!
Если вы используете Redux, я бы рекомендовал использовать пакет npm react-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
    );
  }
}
я смог сделать это, используя 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": "test@test.com"
        }
        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 to postData на index.jsx, я смог получить доступ this.props.history на request.js... затем я могу использовать эту функцию в разных компоненты, просто убедитесь, что я не забыл включить this.postData = postData.bind(this) на constructor().
Использовать Обратный Вызов. У меня получилось!
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'))
вот мой хак (это мой файл корневого уровня, с небольшим redux, смешанным там-хотя я не использую 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() везде, где я хочу (например, в моих функциях магазина redux/thunks/sagas и т. д.), Я надеялся, что смогу просто использовать window.customHistory.push() но почему-то react-router никогда не обновлялся, хотя url-адрес изменился. Но таким образом у меня есть точный экземпляр react-router использует. Я не люблю помещать вещи в глобальный охват, и это один из немногих вещи, с которыми я бы это сделал. Но это лучше, чем любая другая альтернатива, которую я видел IMO.
вы можете использовать его так, как я это делаю для входа в систему и Мэнни разные вещи
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>
)
/*Step 1*/
myFunction(){  this.props.history.push("/home"); }
/**/
 <button onClick={()=>this.myFunction()} className={'btn btn-primary'}>Go 
 Home</button>
шаг один оберните приложение в маршрутизатор
import { BrowserRouter as Router } from "react-router-dom";
ReactDOM.render(<Router><App /></Router>, document.getElementById('root'));
теперь все мое приложение будет иметь доступ к BrowserRouter. Шаг второй я импортирую маршрут, а затем передаю эти реквизиты. Возможно, в одном из ваших основных файлов.
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>
      )}
    />
теперь если я запускаю консоль.журнал(это.реквизит) в моем компонентном 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
кроме того, я просто кодирование bootcamp студент, поэтому я не эксперт, но я знаю, что вы также можете использовать
window.location = "/" //wherever you want to go
поправьте меня, если я ошибаюсь, но когда я проверил это, он перезагрузил всю страницу, которая, как я думал, победила весь смысл использования React.
