【问题标题】:React-router: How to manually invoke Link?React-router:如何手动调用链接?
【发布时间】:2015-05-28 11:28:56
【问题描述】:

我是 ReactJS 和 React-Router 的新手。我有一个组件通过 props 接收来自 react-router<Link/> 对象。每当用户单击此组件内的“下一步”按钮时,我都想手动调用 <Link/> 对象。

现在,我正在使用 refs 来访问 backing instance 并手动单击 <Link/> 生成的“a”标签。

问题:有没有办法手动调用链接(例如this.props.next.go)?

这是我当前的代码:

//in MasterPage.js
var sampleLink = <Link to="/sample">Go To Sample</Link>
<Document next={sampleLink} />

//in Document.js
...
var Document = React.createClass({
   _onClickNext: function() {
      var next = this.refs.next.getDOMNode();
      next.querySelectorAll('a').item(0).click(); //this sounds like hack to me
   },
   render: function() {
      return (
         ...
         <div ref="next">{this.props.next} <img src="rightArrow.png" onClick={this._onClickNext}/></div>
         ...
      );
   }
});
...

这是我想要的代码:

//in MasterPage.js
var sampleLink = <Link to="/sample">Go To Sample</Link>
<Document next={sampleLink} />

//in Document.js
...
var Document = React.createClass({
   render: function() {
      return (
         ...
         <div onClick={this.props.next.go}>{this.props.next.label} <img src="rightArrow.png" /> </div>
         ...
      );
   }
});
...

【问题讨论】:

    标签: javascript reactjs react-router


    【解决方案1】:

    这里的答案已经过时了。

    反应路由器 6

    useHistory 已弃用 v6 使用 useNavigate 挂钩。

    import { useNavigate } from 'react-router-dom'
    
    const navigate = useNavigate()
    
    navigate(`/somewhere`, { replace: true })
    

    【讨论】:

      【解决方案2】:

      如果您想extend Link 组件以利用其 onClick() 处理程序中的一些逻辑,请按以下步骤操作:

      import React from 'react';
      import { Link } from "react-router-dom";
      
      // Extend react-router-dom Link to include a function for validation.
      class LinkExtra extends Link {
        render() {
          const linkMarkup = super.render();
          const { validation, ...rest} = linkMarkup.props; // Filter out props for <a>.
          const onclick = event => {
            if (!this.props.validation || this.props.validation()) {
              this.handleClick(event);
            } else {
              event.preventDefault();
              console.log("Failed validation");
            }
          }
      
          return(
            <a {...rest} onClick={onclick} />
          )
        }
      }
      
      export default LinkExtra;
      

      用法

      <LinkExtra to="/mypage" validation={() => false}>Next</LinkExtra>
      

      【讨论】:

        【解决方案3】:

        React Router v5 - 带有 Hooks 的 React 16.8+(更新于 2020 年 9 月 23 日)

        如果您使用 React Hooks,则可以利用来自 React Router v5 的 useHistory API。

        import React, {useCallback} from 'react';
        import {useHistory} from 'react-router-dom';
        
        export default function StackOverflowExample() {
          const history = useHistory();
          const handleOnClick = useCallback(() => history.push('/sample'), [history]);
        
          return (
            <button type="button" onClick={handleOnClick}>
              Go home
            </button>
          );
        }
        

        如果您不想使用useCallback,另一种编写点击处理程序的方法

        const handleOnClick = () => history.push('/sample');
        

        React Router v4 - 重定向组件

        v4 推荐的方法是允许您的渲染方法捕获重定向。使用 state 或 props 来确定是否需要显示重定向组件(然后触发重定向)。

        import { Redirect } from 'react-router';
        
        // ... your class implementation
        
        handleOnClick = () => {
          // some action...
          // then redirect
          this.setState({redirect: true});
        }
        
        render() {
          if (this.state.redirect) {
            return <Redirect push to="/sample" />;
          }
        
          return <button onClick={this.handleOnClick} type="button">Button</button>;
        }
        

        参考:https://reacttraining.com/react-router/web/api/Redirect

        React Router v4 - 参考路由器上下文

        您还可以利用暴露给 React 组件的 Router 的上下文。

        static contextTypes = {
          router: PropTypes.shape({
            history: PropTypes.shape({
              push: PropTypes.func.isRequired,
              replace: PropTypes.func.isRequired
            }).isRequired,
            staticContext: PropTypes.object
          }).isRequired
        };
        
        handleOnClick = () => {
          this.context.router.push('/sample');
        }
        

        这就是&lt;Redirect /&gt; 的工作原理。

        参考:https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Redirect.js#L46,L60

        React Router v4 - 外部改变历史对象

        如果您仍需要执行类似于 v2 的实现,您可以创建 BrowserRouter 的副本,然后将 history 公开为可导出常量。下面是一个基本示例,但如果需要,您可以组合它以使用可定制的道具注入它。生命周期有一些注意事项,但它应该始终重新渲染路由器,就像在 v2 中一样。这对于来自操作函数的 API 请求后的重定向很有用。

        // browser router file...
        import createHistory from 'history/createBrowserHistory';
        import { Router } from 'react-router';
        
        export const history = createHistory();
        
        export default class BrowserRouter extends Component {
          render() {
            return <Router history={history} children={this.props.children} />
          }
        }
        
        // your main file...
        import BrowserRouter from './relative/path/to/BrowserRouter';
        import { render } from 'react-dom';
        
        render(
          <BrowserRouter>
            <App/>
          </BrowserRouter>
        );
        
        // some file... where you don't have React instance references
        import { history } from './relative/path/to/BrowserRouter';
        
        history.push('/sample');
        

        最新BrowserRouter延长:https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/BrowserRouter.js

        反应路由器 v2

        将新状态推送到browserHistory 实例:

        import {browserHistory} from 'react-router';
        // ...
        browserHistory.push('/sample');
        

        参考:https://github.com/reactjs/react-router/blob/master/docs/guides/NavigatingOutsideOfComponents.md

        【讨论】:

        • hashHistory.push('/sample');如果您使用的是 hashHistory 而不是 browserHistory
        • 这在 material-ui 库中特别有用,因为使用 containerElement={} 并不总是调用链接
        • 注意重定向选项必须指定push(即)。默认情况下,它会进行替换,这与手动调用链接完全不同
        • 重定向对我不起作用,但 withRouter 的 aw04 解决方案更简单且有效
        【解决方案4】:

        5.x版中,可以使用react-router-domuseHistory钩子:

        // Sample extracted from https://reacttraining.com/react-router/core/api/Hooks/usehistory
        import { useHistory } from "react-router-dom";
        
        function HomeButton() {
          const history = useHistory();
        
          function handleClick() {
            history.push("/home");
          }
        
          return (
            <button type="button" onClick={handleClick}>
              Go home
            </button>
          );
        }
        

        【讨论】:

        • 这是最好的解决方案。如果添加一些条件逻辑,可以避免用户多次单击同一个按钮时历史记录中的重复条目:if ((routerHistory.location.pathname + routerHistory.location.search) !== navigateToPath) { routerHistory.push(navigateToPath); }
        • 我需要声明history 变量,挂钩规则不允许调用useHistory().push 的目录
        • 这似乎是最现代的 react-ishy 解决方案。
        • 这很简洁 ?并且适用于 v5.x 的偏离路线使用箭头功能,您可以简单地进一步作为 onClick={ () =&gt; history.push('/home') }
        【解决方案5】:

        React Router 4 包含一个withRouterHOC,它允许您通过this.props 访问history 对象:

        import React, {Component} from 'react'
        import {withRouter} from 'react-router-dom'
        
        class Foo extends Component {
          constructor(props) {
            super(props)
        
            this.goHome = this.goHome.bind(this)
          }
        
          goHome() {
            this.props.history.push('/')
          }
        
          render() {
            <div className="foo">
              <button onClick={this.goHome} />
            </div>
          }
        }
        
        export default withRouter(Foo)
        

        【讨论】:

        • 这对我有用,它看起来像最简单的解决方案。
        • 这是最好的解决方案。我不明白为什么它的票数这么少。
        • 是的,你可以点击链接几次,浏览器返回将无法工作。您需要在浏览器上点击几次才能真正返回
        • @VladyslavTereshyn 你可以添加一些条件逻辑: if ((this.props.location.pathname + this.props.location.search) !== navigateToPath) { ... }
        • 搜索了一个小时后,这个解决方案终于奏效了。谢谢!
        【解决方案6】:

        这又是 JS :) 这仍然有效 ....

        var linkToClick = document.getElementById('something');
        linkToClick.click();
        
        <Link id="something" to={/somewhaere}> the link </Link>
        

        【讨论】:

          【解决方案7】:

          反应路由器 4

          您可以在 v4 中通过上下文轻松调用 push 方法:

          this.context.router.push(this.props.exitPath);

          上下文在哪里:

          static contextTypes = {
              router: React.PropTypes.object,
          };
          

          【讨论】:

          • 使用BrowserRouter,我的组件的上下文对象不包含router 对象。我做错什么了吗?
          • 您是否在组件中设置上下文(上面的第二个块)?
          • 感谢您的参与!最终这对我有用:router: React.PropTypes.object.isRequired。我不知道为什么没有 isRequired 键它不起作用。此外,&lt;Link&gt; 似乎能够获得 history 上下文,但我无法复制它。
          • 有趣的一个 - 如果你放了一个 codepen,如果你仍然卡住,我可以帮你调试它
          • 看来你可以在 React Router v4 中使用this.props.history.push()。我只是通过检查 React Router 传入的 props 才发现这一点的。它似乎有效,但我不确定这是否是个好主意。
          【解决方案8】:

          好的,我想我已经找到了合适的解决方案。

          现在,我没有将 &lt;Link/&gt; 作为 prop 发送到 Document,而是发送 &lt;NextLink/&gt;,这是 react-router Link 的自定义包装器。通过这样做,我可以将右箭头作为链接结构的一部分,同时仍然避免在 Document 对象中包含路由代码。

          更新后的代码如下:

          //in NextLink.js
          var React = require('react');
          var Right = require('./Right');
          
          var NextLink = React.createClass({
              propTypes: {
                  link: React.PropTypes.node.isRequired
              },
          
              contextTypes: {
                  transitionTo: React.PropTypes.func.isRequired
              },
          
              _onClickRight: function() {
                  this.context.transitionTo(this.props.link.props.to);
              },
          
              render: function() {
                  return (
                      <div>
                          {this.props.link}
                          <Right onClick={this._onClickRight} />
                      </div>  
                  );
              }
          });
          
          module.exports = NextLink;
          
          ...
          //in MasterPage.js
          var sampleLink = <Link to="/sample">Go To Sample</Link>
          var nextLink = <NextLink link={sampleLink} />
          <Document next={nextLink} />
          
          //in Document.js
          ...
          var Document = React.createClass({
             render: function() {
                return (
                   ...
                   <div>{this.props.next}</div>
                   ...
                );
             }
          });
          ...
          

          P.S:如果您使用的是最新版本的 react-router,您可能需要使用 this.context.router.transitionTo 而不是 this.context.transitionTo。此代码适用于 react-router 版本 0.12.X。

          【讨论】:

            【解决方案9】:

            https://github.com/rackt/react-router/blob/bf89168acb30b6dc9b0244360bcbac5081cf6b38/examples/transitions/app.js#L50

            或者你甚至可以尝试执行 onClick 这个(更暴力的解决方案):

            window.location.assign("/sample");
            

            【讨论】:

            • 随着代码行的变化,如果您复制详细信息并在此处解释您的答案,您的答案会更好。另外,assign 不是属性,而是函数。
            • (但您仍然只有指向文件特定行的链接)。请在您的回答中包含具体建议,而不仅仅是链接。
            • 感谢您的回答@grechut。但是,我想确保 Document 对路由器一无所知。我期待的行为是:'如果用户点击右箭头,调用下一个函数'。下一个函数可能是链接,也可能不是。
            • 我在 React 之外处理了几个页面(带有 FB 和 Google 重定向的登录屏幕),所以我需要在这些页面的导航中使用它,因为“browserHistory.push('/home');”只更改了 URL,它无法路由页面。谢谢。
            • 这将重新加载页面@grechut,而不是具有反应路由器的应用程序的预期行为。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2017-05-26
            • 2017-08-31
            • 2017-04-29
            • 2021-07-28
            • 2018-09-05
            • 2016-02-20
            • 1970-01-01
            相关资源
            最近更新 更多