Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Warning: Failed Context Types: Required context `router` was not specified in `Component`

I'm trying to test a React component that requires the react-router in separately from app.js.

I have a component that does a redirect using the mixin Router.Navigation like so:

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;

I tried to write a test for this but ran into a wall. Apart from the fact that I'm unable to test that transitionTo works as expected, I've also encountered this error message in my Jest tests:

Warning: Failed Context Types: Required context router was not specified in Searchbar.

Does anyone know how I can get rid of the warning and bonus question, how I can test that the transition works as expected?

I've done research into this and this conversation on Github here: https://github.com/rackt/react-router/issues/400 is the closest I've found to the problem. It looks like I need to export the router separately but that seems like a lot of overhead to just run component tests without the warning a la https://github.com/rackt/react-router/blob/master/docs/guides/testing.md

Is that really the way to go?

like image 362
alengel Avatar asked May 14 '15 12:05

alengel


2 Answers

In version 0.13 of React Router, the mixins Navigation and State were deprecated. Instead, the methods they provide exist on the object this.context.router. The methods are no longer deprecated, but if you're using this.context.router explicitly you don't need the mixin (but you need to declare the contextTypes directly); or, you can use the mixin, but don't need to use this.context.router directly. The mixin methods will access it for you.

In either case, unless you render your component via React Router (via Router#run), the router object is not supplied to the context, and of course you cannot call the transition method. That's what the warning is telling you—your component expects the router to be passed to it, but it can't find it.

To test this in isolation (without creating a router object or running the component through Router#run), you could place a mocked router object on the component's context in the correct place, and test that you call transitionTo on it with the correct value.

like image 70
Michelle Tilley Avatar answered Oct 24 '22 00:10

Michelle Tilley


Because the router relies heavily on the lesser known context feature of React you need to stub it like described 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} />
    }
  });
};

And use like:

var stubRouterContext = require('./stubRouterContext');
var IndividualComponent = require('./IndividualComponent');
var Subject = stubRouterContext(IndividualComponent, {someProp: 'foo'});
React.render(<Subject/>, testElement);
like image 4
Yevgen Safronov Avatar answered Oct 24 '22 02:10

Yevgen Safronov