Как нажать на историю в 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 Router FAQ.
React маршрутизатор 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 }
.
использовать
context
APIиспользование контекста может быть одним из самые простые решения, но будучи экспериментальным 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');
для перенаправления на страницу корзины он будет сохранен в объекте истории.Наслаждайтесь, Майкл.
По данным реагировать маршрутизатор документации В4 - глубокая интеграция Redux на сессии
глубокая интеграция необходима для:
"уметь ориентироваться по диспетчерским действиям"
однако они рекомендуют этот подход в качестве альтернативы "глубокой интеграции":
" вместо отправки действий для навигации вы можете передать объект истории, предоставленный для маршрутизации компонентов к вашим действиям и навигации с ним там же."
таким образом, вы можете обернуть свой компонент с помощью компонента высокого порядка withRouter:
export default withRouter(connect(null, { actionCreatorName })(ReactComponent));
который передаст API истории в реквизит. Таким образом, вы можете вызвать создателя действия, передающего историю как парам. Например, внутри вашего 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
не будет работать.мне удалось заставить толчок работать так:
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
я импортирую историю и использую pushLaterimport history from './history' export const login = createAction( ... history.pushLater({ pathname: PATH_REDIRECT_LOGIN }) ...)
таким образом, я могу нажать на новые действия после вызовов API.
надеюсь, что это помогает!
Если вы используете Redux, то я бы рекомендовал использовать пакет npm react-router-redux. Это позволяет отправлять Redux хранить навигационные действия.
вы должны создать магазин, как описано в их Readme file.
самый простой вариант использования:
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
доpostData
inindex.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 store/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.