В текущей версии React Router (v3) я могу принять ответ сервера и использовать browserHistory.push
, чтобы перейти на соответствующую страницу ответа. Тем не менее, это не доступно в версии 4, и я не уверен, какой способ справиться с этим.
В этом примере, используя 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
});
}
Как я могу сделать перенаправление на страницу Cart из функции React Router v4?
Вы можете использовать методы history
вне ваших компонентов. Попробуйте следующим образом.
Сначала создайте объект history
использующий пакет истории:
// src/history.js
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
Затем заверните его в <Router>
(обратите внимание, что вместо import { BrowserRouter as 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: вы также можете увидеть несколько другой пример в FAQ по React Router.
history.push('some path')
URL-адрес меняется, но страница не меняется. Я должен поместить window.location.reload()
после него в некоторые части моего кода, чтобы заставить его работать. Тем не менее, в одном случае я должен сохранить избыточное дерево состояний, а перезагрузка уничтожает его. Любое другое решение?
withRouter
высшего порядка withRouter
.
React Router v4 принципиально отличается от v3 (и ранее), и вы не можете делать browserHistory.push()
как вы привыкли.
Это обсуждение кажется связанным, если вы хотите получить дополнительную информацию:
- Создание новой
browserHistory
не будет работать, потому что<BrowserRouter>
создает свой собственный экземпляр истории и прослушивает изменения. Таким образом, другой экземпляр изменит URL-адрес, но не обновит<BrowserRouter>
.browserHistory
не подвергается реакции-маршрутизатора в v4, только в v2.
Вместо этого у вас есть несколько вариантов:
withRouter
высокого порядка с withRouter
Вместо этого вы должны использовать withRouter
высокого порядка 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
и ближайшемуmatch
<Route>
помощью компонента более высокого порядка с Router. withRouter будет повторно отображать свой компонент каждый раз, когда маршрут изменяется с теми же реквизитами, что и реквизит<Route>
render:{ 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');
для перенаправления на страницу корзины он будет сохранен в объекте истории.
Наслаждайтесь, Майкл.
Согласно документации React Router v4 - сеанс глубокой интеграции Redux
Глубокая интеграция необходима для:
"иметь возможность перемещаться по диспетчерским действиям"
Однако они рекомендуют этот подход в качестве альтернативы "глубокой интеграции":
"Вместо того, чтобы отправлять действия для навигации, вы можете передать объект истории, предоставленный для маршрутизации компонентов к вашим действиям, и перемещаться вместе с ним".
Таким образом, вы можете обернуть ваш компонент компонентом высокого порядка 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");
};
}
Неприятный вопрос, занял у меня довольно много времени, но в конце концов я решил это так:
Оберните свой контейнер с помощью 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.
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")
}
}
Я предлагаю еще одно решение в случае, если он достоин кого-то другого.
У меня есть файл 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.
Надеюсь, что это поможет!
В этом случае вы передаете реквизит на свой бит. Поэтому вы можете просто позвонить
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')
})
}
}
Использовать обратный вызов. Это сработало для меня!
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'))
Я смог выполнить это, используя 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()
.
Если вы используете 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
);
}
}
next
версию, которая все еще находится в стадии разработки!
Простейшим способом в React Router 4 является использование
this.props.history.push('/new/url');
Но чтобы использовать этот метод, ваш существующий компонент должен иметь доступ к объекту history
. Мы можем получить доступ по
Если ваш компонент связан с Route
напрямую, то ваш компонент уже имеет доступ к объекту history
.
например:
<Route path="/profile" component={ViewProfile}/>
Здесь ViewProfile
имеет доступ к history
.
Если не подключен к Route
напрямую.
например:
<Route path="/users" render={() => <ViewUsers/>}
Затем мы должны использовать withRouter
, функцию более высокого порядка, чтобы деформировать существующий компонент.
Компонент внутри ViewUsers
import { withRouter } from 'react-router-dom';
export default withRouter(ViewUsers);
Что теперь, ваш компонент ViewUsers
имеет доступ к объекту history
.
this.props.history
) происходит из HOC, что означает, что он не связан напрямую с Route
, как вы объясняете.
React router V4 теперь позволяет использовать опору истории, как показано ниже:
this.props.history.push("/dummy",value)
Затем значение может быть доступно везде, где доступна поддержка местоположения как state:{value}
не состояние компонента.
Вот мой хак (это мой файл с корневым уровнем, с небольшим количеством редуксов, смешанных там, хотя я не использую 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()
везде, где захочу (например, в моих функциях хранения редукции /thunks/sagas и т.д.). Я надеялся, что могу просто использовать window.customHistory.push()
, но по какой-то причине react-router
никогда не обновлялся хотя URL-адрес изменился. Но таким образом я использую EXACT-экземпляр react-router
. Я не люблю вкладывать вещи в глобальную сферу, и это одна из немногих вещей, с которыми я бы это сделал. Но это лучше, чем любая другая альтернатива, которую я видел в ИМО.
шаг 1 завершите ваше приложение в Router
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
Кроме того, я всего лишь студентка по кодированию bootcamp, поэтому я не эксперт, но я знаю, что вы также можете использовать
window.location = "/" //wherever you want to go
Исправьте меня, если я ошибаюсь, но когда я проверил это, он перезагрузил всю страницу, которая, как я думал, нанесла поражение цели использования React.
/*Step 1*/
myFunction(){ this.props.history.push("/home"); }
/**/
<button onClick={()=>this.myFunction()} className={'btn btn-primary'}>Go
Home</button>
вы можете использовать его так, как я делаю это для входа и мужественных разных вещей
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>
)
context
для передачи того, что вам нужно, вручную - это «не пойдет». Если я не являюсь автором библиотеки, не должно быть необходимости использовать его. Фактически, Facebook рекомендует против этого.