如何在React路由器v4中推送到历史?

在当前版本的React路由器(v3)中,我可以接受服务器响应并使用browserHistory.push转到相应的响应页面。但是,这在v4中是不可用的,我不确定处理它的适当方法是什么。

在本例中,使用Redux,当用户提交表单时,组件/ app-product-form.js调用this.props.addProduct(props)。当服务器返回成功时,用户将被带到Cart页面。

// 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的函数重定向到购物车页面?

944990 次浏览

React Router v4从根本上不同于v3(以及更早的版本),你不能像以前那样做browserHistory.push()

如果你想了解更多信息,这个讨论似乎是相关的:

  • 创建一个新的browserHistory将不起作用,因为<BrowserRouter>创建了自己的历史实例,并侦听其上的更改。因此,不同的实例将更改url,但不更新<BrowserRouter>
  • browserHistory不会在v4中由react-router公开,只在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);
    

    查看< >强官方文档< / >强以获得更多信息:

    你可以通过withRouter高阶组件访问history对象的属性和最接近的<Route>对象的match。每次路由改变时,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");
    }
    ...
    }
    

    看一下context上的< >强官方文档< / >强:

    如果希望应用程序稳定,就不要使用上下文。这是一个实验性的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');重定向到购物车页面,它将被保存在历史对象中。

享受,迈克尔。

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包react-router-redux。它允许你分派Redux存储导航操作

你必须按照它们的自述文件中描述的那样创建存储。

最简单的用例:

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
);
}
}

在这种情况下,你将道具传递给你的坦克。所以你可以简单地打电话

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文档- Redux深度集成会话

深度整合需要:

“能够通过调度操作进行导航”

然而,他们推荐这种方法作为“深度集成”的替代方案:

“你可以将提供给路由组件的历史对象传递给你的操作,而不是分派操作来导航。”

所以你可以用withRouter高阶组件来包装你的组件:

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

它将历史API传递给道具。你可以调用动作创建者传递历史作为参数。例如,在你的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存储函数/ thks /sagas等),我希望我可以只使用window.customHistory.push(),但出于某种原因,react-router似乎从未更新,即使url改变了。但是这样我就有了react-router使用的EXACT实例。我不喜欢把东西放在全球范围内,这是我做过的为数不多的事情之一。但在我看来,这比我见过的任何其他选择都要好。

你可以在组件外部使用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('/');
});
};
}

乌利希期刊指南:你还可以在React路由器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路由器v4.x有效。

我再提供一个解决方案,以防它对其他人有用。

我有一个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调用之后推送到新的操作。

希望能有所帮助!

我可以通过使用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绑定到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>
)}
/>

现在如果我在我的组件js文件中运行console.log(this.props),我应该得到类似这样的东西

{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路由器V4现在允许历史道具如下所示:

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

该值可以在位置道具可用的任何地方访问 state:{value}不是组件状态

React Router 4中最简单的方法是使用

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

但是要使用这个方法,你的现有的组件应该可以访问history对象。我们可以通过

  1. 如果你的组件直接链接到Route,那么你的组件已经可以访问history对象。

    例如:

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

    这里ViewProfile可以访问history.

    . 0
  2. 如果没有直接连接到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

例如:

<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

如果你想在将函数作为值传递给组件的道具时使用历史,使用react-router 4,你可以简单地在<Route/>组件的渲染属性中解构history道具,然后使用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");
}

当然,如果不需要async,可以使用更简单的动作创建器

小心不使用react-router@5.2.0react-router-dom@5.2.0history@5.0.0。URL将在history.push或任何其他推送历史指令后更新,但导航不能使用react-router。使用npm install history@4.10.1来更改历史版本。看到升级到v5后,React路由器无法工作

我认为这个问题发生在推动历史发生的时候。例如,在NavLink.js中使用<NavLink to="/apps">时,会遇到消耗<RouterContext.Consumer>的问题。当推送到history发生时,context.location将更改为具有动作和位置属性的对象。因此currentLocation.pathname为空以匹配路径。

由于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>
)
}