【问题标题】:React Warning: Failed Context Types: Required context `router` was not specified in `Component`反应警告:失败的上下文类型:所需的上下文“路由器”未在“组件”中指定
【发布时间】:2015-07-26 01:07:48
【问题描述】:

我正在尝试测试一个需要 react-router 与 app.js 分开的 React 组件。

我有一个使用 mixin Router.Navigation 进行重定向的组件,如下所示:

var React = require('react'),
    Router = require('react-router');

var Searchbar = React.createClass({

  mixins : [Router.Navigation],

  searchTerm: function(e) {
    if (e.keyCode !== 13) {
      return;
    }

    this.context.router.transitionTo('/someRoute/search?term=' + e.currentTarget.value)
  },

  render: function() {
    return (
      <div className="searchbar-container">
        <input type="search" placeholder="Search..." onKeyDown={this.searchTerm} />
      </div>
    )
  }
});

module.exports = Searchbar;

我试图为此编写一个测试,但碰壁了。除了我无法测试 transitionTo 是否按预期工作之外,我还在 Jest 测试中遇到了以下错误消息:

警告:上下文类型失败:router 未在 Searchbar 中指定所需的上下文。

有谁知道我如何摆脱警告和奖励问题,如何测试转换是否按预期工作?

我已经对此和 Github 上的对话进行了研究:https://github.com/rackt/react-router/issues/400 是我发现的最接近该问题的。看起来我需要单独导出路由器,但是在没有警告的情况下运行组件测试似乎有很多开销https://github.com/rackt/react-router/blob/master/docs/guides/testing.md

真的是这样吗?

【问题讨论】:

  • 您找到解决方案了吗?
  • 没有,我已经成功了,但我仍在努力。这不是特别紧急,因为它们只是警告消息,但它们很烦人。不过我会整理一下。
  • 嗨@Tom 我终于找到了解决方案,因为 BinaryMuse 的最后一段让我走上了正轨。我用代码发布了自己的答案。
  • 谢谢你让我知道伙计!

标签: reactjs react-router jestjs


【解决方案1】:

在 React Router 的 0.13 版本中,mixin NavigationState 已被弃用。相反,它们提供的方法存在于对象this.context.router 上。这些方法不再被弃用,但如果您明确使用this.context.router,则不需要mixin(但您需要直接声明contextTypes);或者,您可以使用 mixin,但不需要直接使用 this.context.router。 mixin 方法将为您访问它。

在任何一种情况下,除非你通过 React Router 渲染你的组件(通过Router#run),router 对象不会提供给上下文,当然你不能调用转换方法。这就是警告告诉你的——你的组件期望路由器被传递给它,但它找不到它。

要单独测试(不创建路由器对象或通过Router#run 运行组件),您可以将模拟的router 对象放在组件上下文的正确位置,并测试您调用transitionTo使用正确的值。

【讨论】:

【解决方案2】:

因为 router 严重依赖于 React 鲜为人知的 context 功能,所以您需要像 here 中描述的那样对它进行存根

var stubRouterContext = (Component, props, stubs) => {
  return React.createClass({
    childContextTypes: {
      makePath: func,
      makeHref: func,
      transitionTo: func,
      replaceWith: func,
      goBack: func,
      getCurrentPath: func,
      getCurrentRoutes: func,
      getCurrentPathname: func,
      getCurrentParams: func,
      getCurrentQuery: func,
      isActive: func,
    },

    getChildContext () {
      return Object.assign({
        makePath () {},
        makeHref () {},
        transitionTo () {},
        replaceWith () {},
        goBack () {},
        getCurrentPath () {},
        getCurrentRoutes () {},
        getCurrentPathname () {},
        getCurrentParams () {},
        getCurrentQuery () {},
        isActive () {},
      }, stubs);
    },

    render () {
      return <Component {...props} />
    }
  });
};

并像这样使用:

var stubRouterContext = require('./stubRouterContext');
var IndividualComponent = require('./IndividualComponent');
var Subject = stubRouterContext(IndividualComponent, {someProp: 'foo'});
React.render(<Subject/>, testElement);

【讨论】:

    【解决方案3】:

    这是我的 Jest 文件,可以完整回答这个问题。 BinaryMuse 的最后一段让我走上了正轨,但我发现代码示例总是最有帮助的,所以在这里供将来参考。

    jest.dontMock('./searchbar');
    
    describe('Searchbar', function() {
      var React = require('react/addons'),
          Searchbar = require('../../components/header/searchbar'),
          TestUtils = React.addons.TestUtils;
    
      describe('render', function() {
        var searchbar;
    
        beforeEach(function() {
          Searchbar.contextTypes = {
            router: function() {
              return {
                transitionTo: jest.genMockFunction()
              };
            }
          };
    
          searchbar = TestUtils.renderIntoDocument(
            <Searchbar />
          );
        });
    
        it('should render the searchbar input', function() {
          var searchbarContainer = TestUtils.findRenderedDOMComponentWithClass(searchbar, 'searchbar-container');
    
          expect(searchbarContainer).toBeDefined();
          expect(searchbarContainer.props.children.type).toEqual('input');
        });
    
      });
    
    });
    

    希望这对将来的其他人有所帮助。

    【讨论】:

      【解决方案4】:

      我的回答不是专门针对 Jest 的,但它可能会帮助遇到同样问题的人。 我创建了一个类来包装routercontext

      然后在您的测试中添加
      &lt;ContextWrapper&gt;&lt;YourComponent/&gt;&lt;/ContextWrapper&gt;

      包装其他内容(例如ReactIntl)会很有用。

      请注意,您将失去使用浅渲染的可能性,但ReactIntl 已经是这种情况。

      希望对某人有所帮助。


      ContextWrapper.js

      import React from 'react';
      
      export default React.createClass({
          childContextTypes: {
              router: React.PropTypes.object
          },
      
          getChildContext () {
              return {
                  router: {}
              };
          },
      
          render () {
              return this.props.children;
          }
      });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-12-25
        • 2017-12-21
        • 1970-01-01
        • 2016-02-10
        • 1970-01-01
        • 2018-09-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多