ChatGPT解决这个技术问题 Extra ChatGPT

如何在 React Router v4 中推送到历史记录?

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

在此示例中,使用 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
      });
}

如何从 React Router v4 的功能重定向到购物车页面?

只是从提供的最后一个解决方案和 GitHub 上 React Router 问题中的建议添加到这一点,使用 context 手动传递您需要的东西是“不行的”。除非我是图书馆作者,否则不需要使用它。事实上,Facebook 建议不要这样做。
@Chris您找到解决方案了吗?我需要推送到不同的组件,就像你在这里解释的一样
为什么我们不能使用 windows.location.href= URL?使用它来更改 URL 和重定向有什么问题吗?
@Shan 如果您使用 location.href,我相信您会丢失应用程序状态。它也不是移动友好的。
使用@Shan 的建议,例如( windows.location.href=("/") )在移动设备上的问题为零。按预期工作。 *在我的特定用例中丢失应用程序状态不是问题。

O
Oleg Belostotsky

您可以在组件之外使用 history 方法。通过以下方式尝试。

首先,创建一个使用 the history packagehistory 对象:

// 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 中看到一个稍微不同的示例。


我已经尝试完全按照@OlegBelostotsky 所说的去做,但是在 history.push('some path') 之后,URL 发生了变化,但页面没有变化。我必须在代码的某些部分中将 window.location.reload() 放在它之后才能使其正常工作。但是,在一种情况下,我必须保留 redux 状态树,并且重新加载会破坏它。还有其他解决方案吗?
@idunno 尝试使用 withRouter 高阶组件。
这给我一个错误说明:createBrowserHistory 不是函数。我能做些什么?
很抱歉投反对票:)。虽然这也应该有效,但处理此问题的正确方法是 Chris 的回答:stackoverflow.com/a/42716055/491075
确保您使用的是 docs 中指示的正确版本的历史记录。在将历史 v5 与反应路由器 v5 一起使用时,我遇到了与 @sdabrutas 类似的问题(url 推送到历史但页面没有改变)。将历史降级到 v4 完全解决了这个问题。
D
Denialos

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

如果您想了解更多信息,This discussion 似乎相关:

创建一个新的 browserHistory 将不起作用,因为 创建了它自己的历史实例,并监听它的变化。因此,不同的实例将更改 url 但不会更新 。 browserHistory 在 v4 中没有被 react-router 公开,仅在 v2 中。

相反,您有几个选项可以执行此操作:

使用 withRouter 高阶组件 相反,您应该使用 withRouter 高阶组件,并将其包装到将推送到历史记录的组件中。例如:从“react”导入 React;从“react-router-dom”导入 { withRouter };类 MyComponent 扩展 React.Component { ... myFunction() { this.props.history.push("/some/Path"); } ... } export default withRouter(MyComponent);查看官方文档了解更多信息:您可以通过 withRouter 高阶组件访问历史对象的属性和最近的 匹配项。 withRouter 将在每次路由更改时使用与 渲染道具相同的道具重新渲染其组件:{ match, location, history }。

使用上下文 API 使用上下文可能是最简单的解决方案之一,但作为一个实验性 API,它不稳定且不受支持。只有在其他一切都失败时才使用它。这是一个示例:从“react”导入 React;从“prop-types”导入 PropTypes;类 MyComponent 扩展 React.Component { 静态 contextTypes = { 路由器:PropTypes.object } 构造函数(道具,上下文){ 超级(道具,上下文); } ... myFunction() { this.context.router.history.push("/some/Path"); } ... } 查看有关 context 的官方文档:如果您希望您的应用程序稳定,请不要使用 context。它是一个实验性 API,很可能会在未来的 React 版本中中断。如果您不顾这些警告仍坚持使用上下文,请尝试将您对上下文的使用隔离到一个小区域,并尽可能避免直接使用上下文 API,以便在 API 更改时更容易升级。


是的,我确实尝试过。谢谢你的提问。 :-) 那么如何将上下文引入到这个动作函数中呢?到目前为止,它的出现是未定义的。
我已经研究这个话题几天了,但一直无法让它发挥作用。即使使用上面的示例,我仍然在上下文中不断获得路由器未定义。我目前正在使用 react v15.5.10、react-router-dom v4.1.1、prop-types 15.5.10。与此相关的文档很少而且不是很清楚。
@Stu 这应该可以工作this.context.router.history.push('/path');
这并不能回答所提出的问题,即如何访问组件的 history.push OUTSIDE。在组件之外使用 withRouter 或上下文不是选项。
从 React 16.3 开始,上下文 API 不再是实验性的。 React 的博客文章 React v16.3.0: New lifecycles and context API 以获取有关该版本的更多信息。
a
asifsaho

现在使用 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


是否有任何特定的方法需要进行设置,我正在调用以下 let history = useHistory(); 但收到 Object is not callable 错误,当我尝试查看 useHistory 是 console.log(useHistory) 时,它显示为未定义。使用 "react-router-dom": "^5.0.1"
@steff_bdh 您需要在您的 package.json 文件中将其更新为 "react-router-dom": "^5.0.1" 并运行 'npm install'
不错,但不能在 redux 操作类中使用钩子,因为它们不是 React 组件/函数
您将如何在使用(异步)登录时使用它进行重定向。这是问题=> stackoverflow.com/questions/62154408/…
引用 React 的话:“钩子只能在函数组件的主体内部调用。”
S
Saahithyan Vigneswaran

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

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

但要使用此方法,您的 现有 组件应该有权访问 history 对象。我们可以通过

如果您的组件直接链接到 Route,那么您的组件已经可以访问历史对象。例如: 这里 ViewProfile 可以访问历史记录。如果没有直接连接到 Route。例如: } 然后我们必须使用 withRouter,一个高阶函数来扭曲现有组件。 ViewUsers 组件内部 import { withRouter } from 'react-router-dom';使用Router(ViewUsers)导出默认值;现在就是这样,您的 ViewUsers 组件可以访问历史对象。

更新

2- 在这种情况下,将所有路由 props 传递给您的组件,然后即使没有 HOC,我们也可以从组件访问 this.props.history

例如:

<Route path="/users" render={props => <ViewUsers {...props} />}

出色的!您的第二种方法也对我有用,因为我的组件(需要访问 this.props.history)来自 HOC,这意味着它没有直接链接到 Route,正如您所解释的那样。
请注意,当您使用 history.push 时,第二个参数是状态(请参阅 reactrouter.com/web/api/history)。对于我的情况,我在更新状态的操作之后立即执行了 history.push('/same/route/I/am/currently/on', this.state)。这会导致新状态被添加到历史堆栈中。然后在 compoenntDidMount() 中检查是否定义了 this.props.location.state,如果是,则在返回组件时调用 this.setState(this.props.location.state) 以恢复状态。
这并没有回答原始问题如何在组件之外使用历史记录
Y
YouneL

我是这样做的:

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'); 重定向到购物车页面,它将保存在历史对象中。

享受吧,迈克尔。


是的,看起来你可以在组件中很好地推送。影响在组件外部导航的唯一方法是使用重定向。
这并不能回答所提出的问题,即如何访问组件的 history.push OUTSIDE。在组件之外使用 this.props.history 不是一个选项。
a
alainbex

根据React Router v4 documentation - Redux Deep Integration session

深度集成需要:

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

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

“您可以传递提供的历史对象以将组件路由到您的操作并在那里导航,而不是调度操作来导航。”

所以你可以用 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");
  };
}

S
SherylHohman

讨厌的问题,花了我很多时间,但最终,我以这种方式解决了它:

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 有效。


谢谢,您的 withRouter 解决方案适用于打字稿,但与之前的 import { createBrowserHistory } from 'history' 相比它相当慢,请问有什么想法吗?
D
Diego

我提供了另一种解决方案,以防它对其他人有价值。

我有一个 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 上,我导入 History 并使用 pushLater

import history from './history'
export const login = createAction(
...
history.pushLater({ pathname: PATH_REDIRECT_LOGIN })
...)

这样,我可以在 API 调用后推送新的操作。

希望能帮助到你!


C
Chanda Korat

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

}

这并不能回答所提出的问题,即如何访问组件的 history.push OUTSIDE。在组件之外使用 this.context 不是一个选项。
M
Mohammad

小心不要将 react-router@5.2.0react-router-dom@5.2.0history@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"> 在使用 <RouterContext.Consumer> 的 NavLink.js 中遇到问题。当推送到历史记录时,context.location 正在更改为具有操作和位置属性的对象。所以 currentLocation.pathname 为 null 以匹配路径。


a
aksanoble

在这种情况下,您将道具传递给您的 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')
    })
  }
}

K
Kristjan

我在同一个话题上挣扎。我正在使用 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");
}

如果不需要异步,您当然可以使用更简单的操作创建器


这很棒。非常感谢
J
Joao

这是我的 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 存储函数/thunks/sagas 等中)网址已更改。但是这样我就有了 EXACT 实例 react-router 的用途。我不喜欢将东西放在全球范围内,这是我会做的少数事情之一。但它比我见过的任何其他选择都要好。


B
Black

如果您使用 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
    );
  }
}

与 react-router-redux 一起使用,除非您使用的是 next 版本,该版本目前仍在开发中!
s
skwidbreth

我能够通过使用 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() 中的 6}。


L
LuizAsFight

所以我这样做的方式是: - 我没有使用 history.push 进行重定向,而是使用来自 react-router-domRedirect 组件使用此组件时,您只需传递 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}/>
  }
}

这是正确的,它不会破坏反应渲染周期
P
Priyansh Rastogi

使用回调。它对我有用!

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'))

ס
סטנלי גרונן

React 路由器 V4 现在允许使用 history 属性,如下所示:

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

然后,只要 location 属性作为 state:{value} 而不是组件状态可用,就可以访问该值。


这并不能回答所提出的问题,即如何访问组件的 history.push OUTSIDE。在组件之外使用 this.props.history 不是一个选项。
B
Brijesh Kumar

由于我们已经在 react 路由器 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>
)
}

谢谢,它就像魅力一样。
M
MD SHAYON

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

know more


佚名

第一步将您的应用程序包装在路由器中

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 的全部意义。


j
jadlmir

你可以像我这样使用它来登录和许多不同的事情

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>

)

D
David Bagdasaryan
/*Step 1*/
myFunction(){  this.props.history.push("/home"); }
/**/
 <button onClick={()=>this.myFunction()} className={'btn btn-primary'}>Go 
 Home</button>

无需任何进口!
虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。
N
Nolesh

使用自己的 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');
}

K
Kewal Shah

如果您想在将函数作为值传递给组件的 prop 时使用历史记录,使用 react-router 4 您可以简单地解构 <Route/> 组件的 render 属性中的 history prop,然后然后使用 history.push()

    <Route path='/create' render={({history}) => (
      <YourComponent
        YourProp={() => {
          this.YourClassMethod()
          history.push('/')
        }}>
      </YourComponent>
    )} />

注意:为了让它工作,你应该将 React Router 的 BrowserRouter 组件包裹在你的根组件周围(例如,它可能在 index.js 中)