如何在 React Router v4 中推送到历史记录?
How to push to History in React Router v4?
在当前版本的 React Router (v3) 中,我可以接受服务器响应并使用 browserHistory.push
转到相应的响应页面。但是,这在 v4 中不可用,我不确定处理此问题的适当方法是什么。
在此示例中,使用 Redux,components/app-product-form.js 在用户提交表单时调用 this.props.addProduct(props)
。当服务器 returns 成功时,用户将被带到购物车页面。
// 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 的功能重定向到购物车页面?
React Router v4 从根本上不同于 v3(及更早版本),您不能像以前那样 browserHistory.push()
。
如果您想了解更多信息,This discussion 似乎相关:
- Creating a new
browserHistory
won't work because <BrowserRouter>
creates its own history instance, and listens for changes on that. So a different instance will change the url but not update the <BrowserRouter>
.
browserHistory
is not exposed by react-router in v4, only in 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);
查看 official documentation 了解更多信息:
You can get access to the history
object’s properties and the closest <Route>
's match
via the withRouter higher-order component. withRouter will re-render its component every time the route changes with the same props as <Route>
render props: { 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");
}
...
}
查看上下文中的 official documentation:
If you want your application to be stable, don't use context. It is an experimental API and it is likely to break in future releases of React.
If you insist on using context despite these warnings, try to isolate your use of context to a small area and avoid using the context API directly when possible so that it's easier to upgrade when the API changes.
我是这样做的:
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');
重定向到购物车页面,它将保存在历史对象中。
享受吧,迈克尔。
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")
}
}
如果你正在使用 Redux,那么我会推荐使用 npm package react-router-redux。它允许您调度 Redux 商店导航操作。
您必须按照 Readme file 中的描述创建商店。
最简单的用例:
import { push } from 'react-router-redux'
this.props.dispatch(push('/second page'));
Container/Component的第二个用例:
容器:
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
);
}
}
在这种情况下,您正在将 props 传递给您的 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')
})
}
}
根据React Router v4 documentation - Redux Deep Integration session
深度整合需要:
"be able to navigate by dispatching actions"
但是,他们推荐这种方法作为 "deep integration" 的替代方法:
"Rather than dispatching actions to navigate you can pass the history object provided to route components to your actions and navigate with it there."
所以你可以用 withRouter 高阶组件包装你的组件:
export default withRouter(connect(null, { actionCreatorName })(ReactComponent));
这会将历史 API 传递给 props。所以你可以调用动作创建者将历史作为参数传递。例如,在你的 ReactComponent 中:
onClick={() => {
this.props.actionCreatorName(
this.props.history,
otherParams
);
}}
然后,在你的 actions/index.js:
export function actionCreatorName(history, param) {
return dispatch => {
dispatch({
type: SOME_ACTION,
payload: param.data
});
history.push("/path");
};
}
这是我的 hack(这是我的根级文件,其中混合了一些 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 商店 functions/thunks/sagas 等)我曾希望我可以只使用 window.customHistory.push()
但出于某种原因 react-router
似乎从未更新,即使 url 已更改。但是这样我就有了 react-router
使用的 EXACT 实例。我不喜欢将东西放在全局范围内,这是我会做的为数不多的事情之一。但它比我见过的任何其他替代品都要好。
您可以在组件外部使用 history
方法。通过以下方式尝试。
首先,创建一个history
对象使用the history package:
// 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.
中看到一个略有不同的示例
讨厌的问题,花了我很多时间,但最终,我是这样解决的:
用 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有效。
我提供了另一种解决方案,以防它对其他人有价值。
我有一个 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 调用后推送新操作。
希望对您有所帮助!
我能够使用 bind()
完成此操作。我想单击 index.jsx
中的一个按钮,向服务器 post 发送一些数据,评估响应,然后重定向到 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
绑定到 index.jsx
中的 postData
,我能够访问 request.js
中的 this.props.history
...然后我可以重用它在不同的组件中运行,只需确保我记得在 constructor()
.
中包含 this.postData = postData.bind(this)
你可以像我一样使用它来登录和许多不同的事情
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>
使用回调。它对我有用!
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'))
第一步将您的应用包装在路由器中
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 的全部意义。
React router V4 现在允许 history prop 如下使用:
this.props.history.push("/dummy",value)
然后可以在 location prop 可用的任何地方访问该值
state:{value}
不是组件状态。
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
对象。
更新
2
- 在这种情况下,将所有路由 props
传递给您的组件,然后即使没有 HOC
[= 我们也可以从组件访问 this.props.history
30=]
例如:
<Route path="/users" render={props => <ViewUsers {...props} />}
所以我的做法是:
- 我没有使用 history.push
进行重定向,而是使用 react-router-dom
中的 Redirect
组件
使用此组件时,您只需传递 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}/>
}
}
使用自己的 browserHistory
创建自定义 Router
:
import React from 'react';
import { Router } from 'react-router-dom';
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
const ExtBrowserRouter = ({children}) => (
<Router history={history} >
{ children }
</Router>
);
export default ExtBrowserRouter
接下来,在您定义 Router
的根目录上,使用以下内容:
import React from 'react';
import { /*BrowserRouter,*/ Route, Switch, Redirect } from 'react-router-dom';
//Use 'ExtBrowserRouter' instead of 'BrowserRouter'
import ExtBrowserRouter from './ExtBrowserRouter';
...
export default class Root extends React.Component {
render() {
return (
<Provider store={store}>
<ExtBrowserRouter>
<Switch>
...
<Route path="/login" component={Login} />
...
</Switch>
</ExtBrowserRouter>
</Provider>
)
}
}
最后,在需要的地方导入history
并使用:
import { history } from '../routers/ExtBrowserRouter';
...
export function logout(){
clearTokens();
history.push('/login'); //WORKS AS EXPECTED!
return Promise.reject('Refresh token has expired');
}
现在使用 react-router v5,您可以像这样使用 useHistory 挂钩:
import { useHistory } from "react-router-dom";
function HomeButton() {
let history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}
阅读更多信息:https://reacttraining.com/react-router/web/api/Hooks/usehistory
如果你想在将函数作为值传递给组件的 prop 时使用历史记录,使用 react-router 4 你可以简单地解构 history
prop <Route/>
组件的 render 属性,然后使用 history.push()
<Route path='/create' render={({history}) => (
<YourComponent
YourProp={() => {
this.YourClassMethod()
history.push('/')
}}>
</YourComponent>
)} />
注意:要使其正常工作,您应该将 React Router 的 BrowserRouter 组件包裹在您的根组件周围(例如,它可能在 index.js 中)
我在同一个话题上苦苦挣扎。
我正在使用 react-router-dom 5、Redux 4 和 BrowserRouter。
我更喜欢基于函数的组件和钩子。
你这样定义你的组件
import { useHistory } from "react-router-dom";
import { useDispatch } from "react-redux";
const Component = () => {
...
const history = useHistory();
dispatch(myActionCreator(otherValues, history));
};
你的动作创作者正在关注
const myActionCreator = (otherValues, history) => async (dispatch) => {
...
history.push("/path");
}
如果不需要异步,您当然可以使用更简单的动作创建器
注意不要将 react-router@5.2.0
或 react-router-dom@5.2.0
与 history@5.0.0
一起使用。 URL 将在 history.push
或任何其他推送到历史指令后更新,但导航不适用于 react-router
。使用 npm install history@4.10.1
更改历史版本。参见 React router not working after upgrading to v 5。
我认为这个问题是在推送到历史记录时发生的。例如,使用 <NavLink to="/apps">
在 NavLink.js 中遇到消耗 <RouterContext.Consumer>
的问题。当推送到历史记录时,context.location
正在更改为具有操作和位置属性的对象。因此 currentLocation.pathname
为 null 以匹配路径。
由于我们在 React Router 5 中已经包含了历史记录,因此我们可以参考访问相同的历史记录
import React from 'react';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
function App() {
const routerRef = React.useRef();
const onProductNav = () => {
const history = routerRef.current.history;
history.push("product");
}
return (
<BrowserRouter ref={routerRef}>
<Switch>
<Route path="/product">
<ProductComponent />
</Route>
<Route path="/">
<HomeComponent />
</Route>
</Switch>
</BrowserRouter>
)
}
在 v6 中,应重写此应用以使用导航 API。大多数时候,这意味着将 useHistory 更改为 useNavigate 并更改 history.push 或 history.replace 调用站点。
// This is a React Router v6 app
import { useNavigate } from "react-router-dom";
function App() {
let navigate = useNavigate();
function handleClick() {
navigate("/home");
}
return (
<div>
<button onClick={handleClick}>go home</button>
</div>
);
}
在当前版本的 React Router (v3) 中,我可以接受服务器响应并使用 browserHistory.push
转到相应的响应页面。但是,这在 v4 中不可用,我不确定处理此问题的适当方法是什么。
在此示例中,使用 Redux,components/app-product-form.js 在用户提交表单时调用 this.props.addProduct(props)
。当服务器 returns 成功时,用户将被带到购物车页面。
// 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 的功能重定向到购物车页面?
React Router v4 从根本上不同于 v3(及更早版本),您不能像以前那样 browserHistory.push()
。
This discussion 似乎相关:
- Creating a new
browserHistory
won't work because<BrowserRouter>
creates its own history instance, and listens for changes on that. So a different instance will change the url but not update the<BrowserRouter>
.browserHistory
is not exposed by react-router in v4, only in 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);
查看 official documentation 了解更多信息:
You can get access to the
history
object’s properties and the closest<Route>
'smatch
via the withRouter higher-order component. withRouter will re-render its component every time the route changes with the same props as<Route>
render props:{ 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"); } ... }
查看上下文中的 official documentation:
If you want your application to be stable, don't use context. It is an experimental API and it is likely to break in future releases of React.
If you insist on using context despite these warnings, try to isolate your use of context to a small area and avoid using the context API directly when possible so that it's easier to upgrade when the API changes.
我是这样做的:
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');
重定向到购物车页面,它将保存在历史对象中。
享受吧,迈克尔。
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")
}
}
如果你正在使用 Redux,那么我会推荐使用 npm package react-router-redux。它允许您调度 Redux 商店导航操作。
您必须按照 Readme file 中的描述创建商店。
最简单的用例:
import { push } from 'react-router-redux'
this.props.dispatch(push('/second page'));
Container/Component的第二个用例:
容器:
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
);
}
}
在这种情况下,您正在将 props 传递给您的 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')
})
}
}
根据React Router v4 documentation - Redux Deep Integration session
深度整合需要:
"be able to navigate by dispatching actions"
但是,他们推荐这种方法作为 "deep integration" 的替代方法:
"Rather than dispatching actions to navigate you can pass the history object provided to route components to your actions and navigate with it there."
所以你可以用 withRouter 高阶组件包装你的组件:
export default withRouter(connect(null, { actionCreatorName })(ReactComponent));
这会将历史 API 传递给 props。所以你可以调用动作创建者将历史作为参数传递。例如,在你的 ReactComponent 中:
onClick={() => {
this.props.actionCreatorName(
this.props.history,
otherParams
);
}}
然后,在你的 actions/index.js:
export function actionCreatorName(history, param) {
return dispatch => {
dispatch({
type: SOME_ACTION,
payload: param.data
});
history.push("/path");
};
}
这是我的 hack(这是我的根级文件,其中混合了一些 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 商店 functions/thunks/sagas 等)我曾希望我可以只使用 window.customHistory.push()
但出于某种原因 react-router
似乎从未更新,即使 url 已更改。但是这样我就有了 react-router
使用的 EXACT 实例。我不喜欢将东西放在全局范围内,这是我会做的为数不多的事情之一。但它比我见过的任何其他替代品都要好。
您可以在组件外部使用 history
方法。通过以下方式尝试。
首先,创建一个history
对象使用the history package:
// 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.
中看到一个略有不同的示例讨厌的问题,花了我很多时间,但最终,我是这样解决的:
用 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有效。
我提供了另一种解决方案,以防它对其他人有价值。
我有一个 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 调用后推送新操作。
希望对您有所帮助!
我能够使用 bind()
完成此操作。我想单击 index.jsx
中的一个按钮,向服务器 post 发送一些数据,评估响应,然后重定向到 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
绑定到 index.jsx
中的 postData
,我能够访问 request.js
中的 this.props.history
...然后我可以重用它在不同的组件中运行,只需确保我记得在 constructor()
.
this.postData = postData.bind(this)
你可以像我一样使用它来登录和许多不同的事情
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>
使用回调。它对我有用!
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'))
第一步将您的应用包装在路由器中
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 的全部意义。
React router V4 现在允许 history prop 如下使用:
this.props.history.push("/dummy",value)
然后可以在 location prop 可用的任何地方访问该值
state:{value}
不是组件状态。
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
对象。
更新
2
- 在这种情况下,将所有路由 props
传递给您的组件,然后即使没有 HOC
[= 我们也可以从组件访问 this.props.history
30=]
例如:
<Route path="/users" render={props => <ViewUsers {...props} />}
所以我的做法是:
- 我没有使用 history.push
进行重定向,而是使用 react-router-dom
中的 Redirect
组件
使用此组件时,您只需传递 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}/>
}
}
使用自己的 browserHistory
创建自定义 Router
:
import React from 'react';
import { Router } from 'react-router-dom';
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
const ExtBrowserRouter = ({children}) => (
<Router history={history} >
{ children }
</Router>
);
export default ExtBrowserRouter
接下来,在您定义 Router
的根目录上,使用以下内容:
import React from 'react';
import { /*BrowserRouter,*/ Route, Switch, Redirect } from 'react-router-dom';
//Use 'ExtBrowserRouter' instead of 'BrowserRouter'
import ExtBrowserRouter from './ExtBrowserRouter';
...
export default class Root extends React.Component {
render() {
return (
<Provider store={store}>
<ExtBrowserRouter>
<Switch>
...
<Route path="/login" component={Login} />
...
</Switch>
</ExtBrowserRouter>
</Provider>
)
}
}
最后,在需要的地方导入history
并使用:
import { history } from '../routers/ExtBrowserRouter';
...
export function logout(){
clearTokens();
history.push('/login'); //WORKS AS EXPECTED!
return Promise.reject('Refresh token has expired');
}
现在使用 react-router v5,您可以像这样使用 useHistory 挂钩:
import { useHistory } from "react-router-dom";
function HomeButton() {
let history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}
阅读更多信息:https://reacttraining.com/react-router/web/api/Hooks/usehistory
如果你想在将函数作为值传递给组件的 prop 时使用历史记录,使用 react-router 4 你可以简单地解构 history
prop <Route/>
组件的 render 属性,然后使用 history.push()
<Route path='/create' render={({history}) => (
<YourComponent
YourProp={() => {
this.YourClassMethod()
history.push('/')
}}>
</YourComponent>
)} />
注意:要使其正常工作,您应该将 React Router 的 BrowserRouter 组件包裹在您的根组件周围(例如,它可能在 index.js 中)
我在同一个话题上苦苦挣扎。 我正在使用 react-router-dom 5、Redux 4 和 BrowserRouter。 我更喜欢基于函数的组件和钩子。
你这样定义你的组件
import { useHistory } from "react-router-dom";
import { useDispatch } from "react-redux";
const Component = () => {
...
const history = useHistory();
dispatch(myActionCreator(otherValues, history));
};
你的动作创作者正在关注
const myActionCreator = (otherValues, history) => async (dispatch) => {
...
history.push("/path");
}
如果不需要异步,您当然可以使用更简单的动作创建器
注意不要将 react-router@5.2.0
或 react-router-dom@5.2.0
与 history@5.0.0
一起使用。 URL 将在 history.push
或任何其他推送到历史指令后更新,但导航不适用于 react-router
。使用 npm install history@4.10.1
更改历史版本。参见 React router not working after upgrading to v 5。
我认为这个问题是在推送到历史记录时发生的。例如,使用 <NavLink to="/apps">
在 NavLink.js 中遇到消耗 <RouterContext.Consumer>
的问题。当推送到历史记录时,context.location
正在更改为具有操作和位置属性的对象。因此 currentLocation.pathname
为 null 以匹配路径。
由于我们在 React Router 5 中已经包含了历史记录,因此我们可以参考访问相同的历史记录
import React from 'react';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
function App() {
const routerRef = React.useRef();
const onProductNav = () => {
const history = routerRef.current.history;
history.push("product");
}
return (
<BrowserRouter ref={routerRef}>
<Switch>
<Route path="/product">
<ProductComponent />
</Route>
<Route path="/">
<HomeComponent />
</Route>
</Switch>
</BrowserRouter>
)
}
在 v6 中,应重写此应用以使用导航 API。大多数时候,这意味着将 useHistory 更改为 useNavigate 并更改 history.push 或 history.replace 调用站点。
// This is a React Router v6 app
import { useNavigate } from "react-router-dom";
function App() {
let navigate = useNavigate();
function handleClick() {
navigate("/home");
}
return (
<div>
<button onClick={handleClick}>go home</button>
</div>
);
}