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

在...创建的问题 Thu, Mar 16, 2017 12:00 AM

在当前版本的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的功能重定向到Cart页面?

    
258
  1. 只是从最后提供的解决方案和GitHub上的React Router问题中的建议添加到此,使用context手动传递你需要的是“不行” 。除非我是图书馆作者,否则不应该使用它。事实上,Facebook建议反对它。
    2017-03-12 13:58:22Z
  2. @ Chris你找到了解决方案吗?我需要推动一个不同的组件在行动,就像你在这里解释的那样
    2017-06-14 14:20:13Z
  3. @G.G,遗憾的是我没有。最后我读到的是React Training团队维护React Router有一个redux软件包。我没有任何运气使其成功,而且他们没有做太多工作来解决它。
    2017-06-14 15:03:57Z
  4. 醇>
    18答案                              18 跨度>                         

    您可以在组件外部使用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

        
    202
    2018-01-01 09:11:49Z
    1. 我试图像@OlegBelostotsky所说的那样做,但是在history.push('some path')之后,URL发生了变化,但页面没有变化。我必须将window.location.reload()放在我的代码的某些部分后才能使它工作。但是,在一个实例中,我必须保留redux状态树,然后重新加载会破坏它。还有其他解决方案吗?
      2018-03-26 09:47:52Z
    2. @ idunno尝试使用withRouter高阶组件。
      2018-03-26 12:03:06Z
    3. 这给我一个错误说明:createBrowserHistory不是一个函数。我该怎么办?
      2018-11-13 06:54:48Z
    4. @ AKJ也许import createHistory from 'history/createBrowserHistory'; export default createHistory();会起作用。
      2018-11-13 21:32:00Z
    5. 我发现使用带有路径导航方法的简单类比使用react-router组件更合乎逻辑。谢谢你的解决方案!
      2018-11-21 18:52:59Z
    6. 醇>

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

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

      
    • 创建新的browserHistory将无法​​正常工作,因为<BrowserRouter>会创建自己的历史记录实例,并会对其进行更改。因此,不同的实例将更改网址,但不会更新<BrowserRouter>
    •   
    •  v4中的react-router不暴露browserHistory,仅在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> match 通过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");
        }
        ...
      }
      

      在上下文中查看 官方文档 : /p>

        

      如果您希望应用程序稳定,请不要使用上下文。它是一个实验性的API,很可能会在未来的React版本中破解。

           

      如果您坚持使用上下文尽管存在这些警告,请尝试将上下文的使用隔离到一个小区域,并尽可能避免直接使用上下文API,以便在API更改时更容易升级。

    283
    2018-04-26 10:41:29Z
    1. 是的,我确实尝试过。谢谢你的询问。 :-)那么如何将上下文纳入此动作函数?到目前为止,它还是未定义的。
      2017-03-10 16:48:21Z
    2. 我几天来一直在研究这个话题,但却未能将其发挥作用。即使使用上面的示例,我仍然在上下文中未定义路由器。我目前正在使用react v15.5.10,react-router-dom v4.1.1,prop-types 15.5.10。围绕这个的文档是稀疏的,不是很清楚。
      2017-05-17 20:05:27Z
    3. @ Stu这应该可以工作this.context.router.history.push('/path');
      2017-06-15 05:50:20Z
    4. 这不回答被问到的问题,即如何访问组件的history.push OUTSIDE。使用withRouter或上下文不是组件外部的选项。
      2017-11-17 01:38:21Z
    5. 在我的案例中使用withRouter帮助的解决方案,谢谢。
      2018-01-03 11:32:38Z
    6. 醇>

    我就这样做了:

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

    享受,迈克尔。

        
    22
    2018-07-22 14:53:19Z
    1. 是的,看起来在组件中你可以推得很好。影响组件外部导航的唯一方法是使用重定向。
      2017-04-13 00:15:35Z
    2. 这不回答被问到的问题,即如何访问history.push组件的OUTSIDE。在组件外部使用this.props.history不是一个选项。
      2017-11-17 01:38:53Z
    3. 很好,我正在遵循我认为的Udemy课程!
      2019-06-24 23:31:34Z
    4. 醇>

    根据 React Router v4文档 - Redux Deep Integration会话

    需要深度整合:

      

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

    但是,他们建议将此方法作为“深度整合”的替代方案:

      

    “您可以将提供的历史记录对象传递给您的操作并在其中导航,而不是分派导航操作。”

    因此,您可以使用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");
      };
    }
    
        
    19
    2017-08-13 23:51:43Z

    讨厌的问题,花了我很多时间,但最终,我这样解决了:

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

        
    16
    2018-03-12 07:26:36Z

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

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

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

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

      例如:

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

      ViewProfile可以访问history

    2. 如果没有直接连接到Route

      例如:

       
      <Route path="/users" render={() => <ViewUsers/>}
      

      然后我们必须使用withRouter,一个更高的顺序功能来扭曲现有组件。

      内部 ViewUsers组件

      • import { withRouter } from 'react-router-dom';

      • export default withRouter(ViewUsers);

      现在就是这样,你的ViewUsers组件可以访问history对象。

    3. 醇>

      更新强>

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

      例如:

       
      <Route path="/users" render={props => <ViewUsers {...props} />}
      
          
    13
    2019-02-19 14:36:33Z
    1. 第二种方法对我有用。
      2019-01-05 07:05:32Z
    2. 太棒了!你的第二种方法也为我做了伎俩,因为我的组件(需要访问this.props.history)来自HOC,这意味着它没有直接链接到Route,正如你解释的那样。
      2019-02-18 19:34:11Z
    3. 醇>

    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")
        }
    
    }
    
        
    6
    2017-05-18 11:14:59Z
    1. 这不回答t他问的问题是如何访问history.push组件的外部。在组件外部使用this.context不是一个选项。
      2017-11-17 01:40:11Z
    2. 醇>

    在这种情况下,你将道具传递给你的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')
        })
      }
    }
    
        
    5
    2017-08-09 09:04:59Z

    我提供了一个解决方案,以防其他人有价值。

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

    希望它有所帮助!

        
    5
    2017-10-30 12:15:55Z

    我能够通过使用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中的index.jsx,我能够在this.props.history中访问request.js ...然后我可以在不同的组件中重用此功能,只需确保我记得在this.postData = postData.bind(this)中包含constructor()

        
    3
    2017-11-21 16:40:57Z

    使用回调。它对我有用!

     
    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'))
    
        
    3
    2018-03-11 19:53:16Z

    这是我的黑客(这是我的根级文件,其中混合了一些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等)我希望我可以使用window.customHistory.push()但由于某种原因react-router似乎永远不会更新,即使网址已更改。但是这种方式我有react-router使用的EXACT实例。我不喜欢把东西放在全球范围内,这是我做的少数事情之一。但它比我见过IMO的任何其他选择都要好。

        
    2
    2017-08-20 17:32:15Z

    如果您使用的是Redux,那么我建议使用npm包反应路由器-终极版。它允许您调度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
        );
      }
    }
    
        
    2
    2017-09-11 13:04:09Z
    1. 除非你使用的是next版本,否则与react-router-redux一起使用,此版本目前仍在开发中!
      2017-11-24 11:35:45Z
      醇>

      你可以像这样使用它来登录和manny不同的东西

       
      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>
      
      )
      
          
      0
      2018-03-01 09:33:28Z
       
      /*Step 1*/
      myFunction(){  this.props.history.push("/home"); }
      /**/
       <button onClick={()=>this.myFunction()} className={'btn btn-primary'}>Go 
       Home</button>
      
          
      0
      2018-03-10 08:11:07Z
      1. 不需要任何导入!
        2018-03-10 08:12:41Z
      2. 虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的其他背景可提高其长期价值。
        2018-03-10 09:51:12Z
      3. 醇>

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

      中  
      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的整个过程。

          
      0
      2018-09-21 05:49:08Z

      React router V4现在允许使用历史支柱如下:

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

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

          
      0
      2018-11-30 21:37:53Z
      1. 这不回答被问到的问题,即如何访问组件的history.push OUTSIDE。在组件外部使用this.props.history不是一个选项。
        2019-03-11 13:00:24Z
      2. 醇>

      所以我这样做的方式是: - 而不是使用history.push重定向,我只使用Redirect中的react-router-dom组件 使用此组件时,您只需传递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}/>
        }
      }
      
          
      0
      2019-04-01 06:29:40Z
来源放置 这里