React-router: this.context.router is undefined

Created on 21 Mar 2015  Â·  94Comments  Â·  Source: ReactTraining/react-router

I just upgraded to react-router (and react) 0.13, and got rid of all the deprecated State mixins. Using this.context.router doesn't seem to work. I just see this everywhere:

Uncaught TypeError: Cannot read property 'getParams' of undefined

Is there anything additional I need to do?


Edit by Ryan Florence to get people to the answer right here at the top of the issue :dancer:

Check out the upgrade guide: https://github.com/rackt/react-router/blob/master/UPGRADE_GUIDE.md#012x---013x

Sorry it wasn't done w/ the release, it's been an abnormally busy month for us since we quit our jobs and started a new business :P

Most helpful comment

Your class also needs

  contextTypes: {
    router: React.PropTypes.func.isRequired
  },

All 94 comments

Your class also needs

  contextTypes: {
    router: React.PropTypes.func.isRequired
  },

I added that to my class and it seems to have no effect. I get the exact same error.

I get the same thing with react 0.13.1 and react-router 0.13.1. _this.context_ is an empty object, so _this.context.router_ is undefined. A working react-router fiddle would be nice ;) (Removed proptype comment, didn't notice it was _contextTypes_, sorry)

In general, I don't like this switch to relying on context everywhere. I'd rather have a clear, well-defined API that I can use rather than rely on invisible magic that's impossible to debug when it breaks. There's no stacktrace, no actual error anywhere, and no trail leading to anywhere inside of react-router. It's just... _broken_.

Adding PropTypes shouldn't matter because that's just validation.

That's true for props, but not for context. I guess the reasoning behind this is to make people use context in a declarative fashion and not overuse it. Without it, a component making heavy use of context would be very hard to understand and reuse. Unlike with props, you have no idea where the data is coming from.

In general, I don't like this switch to relying on context everywhere. I'd rather have a clear, well-defined API that I can use rather than rely on invisible magic that's impossible to debug when it breaks

I never really liked the usage of context either to be honest. But without it, there's no way the router can provide things like a <Link> that can be used anywhere in your render tree; you would need to pass everything via props.

That's also the reason why the mixins and components have been relying on context under the hood for quite some time. In that sense, I think that the new API is less magical, since it doesn't attempt to hide the usage of context.

Comparing the two

React.createClass({
  mixins: [State],
  someFn: function(){
    this.getParams();
  }
});

React.createClass({
  contextTypes: {
    router: React.PropTypes.func
  },
  someFn: function(){
    this.context.router.getCurrentParams();
  }
});

In my opinion, the context way seems less magical, more declarative and doesn't rely on mixins (which was the main motivation behind this change iirc).

A working react-router fiddle would be nice

I updated the examples.

I see the reasoning behind the change, so I support it, even though it makes the code a little bit uglier. ;)

Adding contextTypes and replacing this.getParams() with this.context.router.getCurrentParams() was all it took to make things work for me, so I'm happy.

I updated the examples.

Where are these fiddles?

In the repo: https://github.com/rackt/react-router/tree/master/examples
Commit: 1b2293b0be7f4c2eeaff0afe9d8c84430ef22e17

To run locally, clone the repo, install npm dependencies and do "npm run examples".

Here's an example High-order Component whose sole purpose is to retrieve the router from the context and expose it in this.props. A bit more future-proof than using mixins or context directly.

function ExposeRouter(ComponentClass) {
  return React.createClass({
    displayName: 'ExposeRouter',

    contextTypes: {
      router: React.PropTypes.func.isRequired,
    },

    render() {
      return <ComponentClass {...this.props} router={this.context.router}/>;
    }
  });
}

Keep using the mixins for now, we're not super excited about this.context.router either. What do you think about injecting this.props.router to your handlers automatically?

Keep using the mixins for now, we're not super excited about this.context.router either.

I suggest removing the deprecation warning then, otherwise it might be confusing for users (like me :wink:).

What do you think about injecting this.props.router to your handlers automatically?

Sounds good to me, although a bit cluttered, right now props really are the only way to safely pass stuff down the tree.

Using version 0.13.1 of react and react-router the following seems to work for me:

import React from 'react';
class LoginPage extends React.Component {
  ...
  onChange() {
    this.context.router.replaceWith('/');
  }
  ...
}

LoginPage.contextTypes = {
  router: React.PropTypes.func.isRequired
};

export default LoginPage;

MariusRumpf +100500

My context object is also empty on 0.13.1.

@MariusRumpf You're a true hero. Thanks man!

Hm, perhaps I'm missing something in @MariusRumpf's implementation. Perhaps someone could create an example with this method?

I'm not sure what's going on here; everyone's just posting examples of code I already have, except mine isn't working.

I did what the warnings said and stopped using mixins. Now I'm being told to start using mixins again. Can we please decide on what the API is, and stick with it?

https://github.com/olegsmetanin/olegsmetanin.github.io/blob/dev/test/tests/Router.es7.jsx
"react": "~0.13.1",
"react-router": "~0.13.1"

import React from 'react';
import Router from 'react-router';
import { Route, RouteHandler, DefaultRoute, State, Link, Redirect } from 'react-router';
import should from 'should';

describe('Router', function () {

  it('router context is available in componentWillMount', (done) => {

    class Widget extends React.Component {
      constructor (props) {
        super(props);
        should.not.exist(this.context);
      }

      componentWillMount() {
        this.context.should.have.property('router');
        this.context.router.getCurrentPath().should.equal('/page/1');
        this.context.router.getCurrentParams().should.have.property('id', '1');
      }

      render() {
        this.context.should.have.property('router');
        this.context.router.getCurrentPath().should.equal('/page/1');
        this.context.router.getCurrentParams().should.have.property('id', '1');
        return <div></div>;
      }
    }

    Widget.contextTypes = {
      router: React.PropTypes.func.isRequired,
    };

    class Page extends React.Component {
      constructor (props) {
        super(props);
        should.not.exist(this.context);
      }

      componentWillMount() {
        this.context.should.have.property('router');
        this.context.router.getCurrentPath().should.equal('/page/1');
        this.context.router.getCurrentParams().should.have.property('id', '1');
      }

      render() {
        this.context.should.have.property('router');
        this.context.router.getCurrentPath().should.equal('/page/1');
        this.context.router.getCurrentParams().should.have.property('id', '1');
        return <div><Widget/></div>;
      }
    }

    Page.contextTypes = {
      router: React.PropTypes.func.isRequired,
    };

    class Layout extends React.Component {
      constructor (props) {
        super(props);
        should.not.exist(this.context);
      }

      componentWillMount() {
        this.context.should.have.property('router');
        this.context.router.getCurrentPath().should.equal('/page/1');
        this.context.router.getCurrentParams().should.have.property('id', '1');
      }

      render() {
        this.context.should.have.property('router');
        this.context.router.getCurrentPath().should.equal('/page/1');
        this.context.router.getCurrentParams().should.have.property('id', '1');
        return <div><RouteHandler/></div>;
      }
    }

    Layout.contextTypes = {
      router: React.PropTypes.func.isRequired,
    };

    let routes = <Route handler={Layout}>
      <Route path="/page/?:id?" handler={Page}/>
    </Route>;

    Router.run(routes, '/page/1', function (Handler) {
      React.renderToString(<Handler />);
      done();
    });

  });

});

@tjwebb My example above is using ES6, in which you cannot use mixins. You have to add the part

Page.contextTypes = {
  router: React.PropTypes.func.isRequired
};

after every class declaration in which you want to use this.context.router.

Yeah this.context is undefined for me too. I'm using react router from the CDN (not sure if this makes a difference) - I've tried all the suggested solutions here and none work. The State Mixin is working though.

Yea I never got it working either. I ended up using Flux for some of my more "manual" transitions, which would require calling those functions.

I think that this.context is empty object because of element is not inside RouteHandler. See the example below:
<div>
<Header />
<div className='view md-padding'>
<RouteHandler />
</div>
</div>

As you can see <Header /> is not inside RouteHandler and therefore I can't use this.context.router. Ok, I can use Router.State mixin instead but I get warning that Router.State is deprecated.
What should I do in this situation (when I want to use Roter (isActive, getParams and other) in components that are not inisde RouteHandler) ?

I think that this.context is empty object because of element is not inside RouteHandler.

No.

Have you tried adding contextTypes as suggested above?

@gaearon I have, and it didn't work for me.

@n1ghtmare Can you reproduce this on a simple github repo?

@gaearon Sure, I would love to - as soon as I have some time. Also before I do that, as I've mentioned before - I'm referencing the react-router from the CDN, is it possible that the version there lags behind?

https://cdnjs.com/libraries/react-router

Oh. New version isn't on CDN yet.

@gaearon Yeah, I had a suspicion! Sorry about that, my bad.

Can we rely on context? It seems that react guys are still think about it because of performance...

And there is a trouble:
contextTypes: {
router: React.PropTypes.object.isRequired
},

Warning: Failed Context Types: Invalid context router of type function supplied to Header, expected object. Check the render method of Viewport.

Yeah, pardon. It needs to be func. My mistake.

contextTypes: {
  router: React.PropTypes.func.isRequired
},

Can we rely on context? It seems that react guys are still think about it because of performance...

Router _already_ relied on context before. It's just made explicit now. Potential perf problems will likely be solved in 0.14. You'll only have those problems if you use PureRenderMixin everywhere, and there are ways to work around those problems so you shouldn't worry too much about them.

Just updated my reference to react router and everything worked as expected. Silly me! It will be nice if someone updates the cdn though. :stuck_out_tongue_winking_eye:

I am using

"react": "^0.12.2", 
"react-router": "^0.12.4"

and this.context looks like this:

Object {router: undefined}

I have added the:

contextTypes: {
        router: React.PropTypes.func.isRequired
    },

but still doesn't work
Any ideas?

Edit:
With 0.13.1 it works and router is not undefined, but my other components support React 0.12.

I was getting hung up on the exact same issue, but upgrading sorted things out for me:

    ├─┬ [email protected]
    ├─┬ [email protected]

Also having this issue, even with react 0.13.1 and react-router 0.13.2.

Strange. I'm not sure what the deal is, but using contextTypes suddenly started working for me. I just rimraffed my node_modules dir (I'm using npm + browserify), reinstalled, and everything works. Sorry I can't be of more help than that.

This context approach still seems like voodoo to me, but my stuff works so I'm done complaining for now :)

Reinstalling node_modules is usually a good tactic :)

I just rimraffed my node_modules dir (I'm using npm + browserify), reinstalled, and everything works.

Yes, you might've had problems with two different React versions installed. It happens.

Yes, you might've had problems with two different React versions installed. It happens.

Won't it be nice when npm 3 lands?

You'll only have those problems if you use PureRenderMixin everywhere

  • @gaearon

I haven't been following context perf. At React Conf, they made PRM sound like a best practice. What's the issue?

I haven't been following context perf. At React Conf, they made PRM sound like a best practice. What's the issue?

It's not perf per se, but that context is currently not friends with shouldComponentUpdate. I expect this to be solved in React 0.14 with something like shouldUpdateChildContext. See https://github.com/facebook/react/issues/2517

Add that to the long list of things I never thought about.

Thanks @gaearon!

So I am having the same problem when trying to use this.context in my constructor:

class Main extends React.Component {

  constructor(props) {
    super(props);
    console.log(this.context) //=> undefined
  }

  // called via onClick
  onSearch(keyword) {
    console.log(this.context) //=> object
  }

  ...
}

It works fine later on.

@appsforartists yes, already have that. It is only in the constructor that this.context is undefined

Context is a second argument to constructor.

On 27 Mar 2015, at 02:14, Sebastian Porto [email protected] wrote:

@appsforartists yes, already have that. It is only in the constructor that this.context is undefined

—
Reply to this email directly or view it on GitHub.

Yes, so this works:

constructor(props, context) {
   super(props);
   console.log(context.router.getCurrentPathname()) //=> not undefined
}

example: http://learnreact.robbestad.com/#/articles/article/1 (check console.log)

Everything works fine until I try to test a component that uses a for example. Before I updated to the new react-router version the tests worked with the help of the Testing Guide. I also updated the stubRouterContext but it get the following warnings:

Warning: Failed Context Types: Invalid context `router` of type `object` supplied to `Link`, expected `function`. Check the render method of `Header`.

@moklick Sorry, testing guide is currently outdated.

@gaearon Ok. Do you have a hint how to fix this? Then I could update the testing guide

@moklick Sorry, was writing from my phone. Docs should be correct now: https://github.com/rackt/react-router/commit/06c15c5b616140589ed56ccc00be84bcbd3de543

@gaearon Thanks! No more warnings anymore.

Does anyone have any other top tips not listed here? I'm having this same problem with react-router 0.13.2 and react 0.13.1. I'm using "old skool" react classes and have the following contextTypes:

contextTypes: {
  router: React.PropTypes.func
},

I've also tried reverting to react-router 0.13.1 and nuking node_modules. Neither helped.

I can confirm the issue was my total failure to be a competent developer. Old version was cached. Where is the blush on this thing... :grin: :blush:

thanks @MariusRumpf! :heart:

ES7 (experimental), provides a bit more sugar:

export default class App {
  static contextTypes = {
    router: PropTypes.func.isRequired
  };
  render() {
    // ..
  };
}

Reference: https://gist.github.com/jeffmo/054df782c05639da2adb
Example: https://github.com/gaearon/flux-react-router-example/blob/fbdef56245a1f9cccdf181eb56acfc9a3f753b5c/scripts/App.js#L7-L10 (see his .babelrc for setup)

thanks @MariusRumpf

I was having issues getting the context inside the class constructor(). Here is the fix I used. Not sure if this is safe or not:

export default class App {
    constructor (props, context) {
        // this.context doesn't work here, so I did this:
        super(props, context);

        // Now I can call my methods as usual:
        this.state = {
            something: this._getSomething()
        }
    }

    static contextTypes = {
        router: PropTypes.func.isRequired
    }

    _getSomething () {
        // This was broken before
        return this.context.router.getCurrentPath() === 'test' ? true : false;
    }
}

@globexdesigns

I'd say a more correct fix is to call super(props, context) from the constructor.

@gaearon Thanks! Updated my comment to reflect that in case someone else runs into it.

added

contextTypes: {
  router: React.PropTypes.func
}

to my class but still get Uncaught TypeError: Cannot read property 'getRouteAtDepth' of undefined
tried different versions and or combinations. deleted npm modules. tried the mixin. nothing works for me. any ideas?

im using:

  • browserify
  • react-router 0.13.3
  • react 0.13.1

module:

var React = require("React");
var Widget = require("./widget.js");
var widgetTopbaugeld = require("./widget2.js");

module.exports = React.createClass({
    contextTypes: {
        router: React.PropTypes.func
    },
    render: function() {
        var div = React.createFactory("div");
        var wid = React.createFactory(Widget);
        return div({},
            wid({
                title: "Top Baugeld",
            }, React.createElement(widgetTopbaugeld, {
                expanded: true,
                itemsPerPage: 50
            }))
        );
    }
});

app

var React = require("React");
var ReactRouter = require("react-router");

module.exports = React.createClass({
    contextTypes: {
        router: React.PropTypes.func
    },
    render: function() {
        return React.createElement("div", null,
            this.props.children,
            React.createElement(ReactRouter.RouteHandler, null)
        );
    }
});

index

var React = require("react");
var ReactRouter = require("react-router");

var AppModule = require("./app.js");
var TopbaugeldModule = require("./topbaugeld.js");

/**
 * Routes
 * @type {[type]}
 */
var routes = (
React.createElement(ReactRouter.Route, {
    handler: AppModule,
    path: "/"
},
    React.createElement(ReactRouter.Route, {
        path: "/top-baugeld",
        handler: TopbaugeldModule
    })
));


// render
ReactRouter.run(routes, ReactRouter.HashLocation, function(Handler) {
    React.render(React.createElement(Handler, {}), document.getElementById('routings'));
});

So, i solved it by moving the "app" Module code into the "index" Module where the routes are defined. So if you´re using browserify, have the same problem and have your "app module" and "route definitions" in different files, try to merge them into one. I think it has something to do with the context. React creates different ones for each of them. So this dev log gave me a hint:

Warning: owner-based and parent-based contexts differ (values:undefinedvs1) for key (routeDepth) while mounting RouteHandler (see: http://fb.me/react-context-by-parent)

moving the modules into one file gives the "owner context aka app module" the same context as "parent-based context aka your route modules".

Still getting this error. I'm using:

  • react 0.13.3
  • react-router 1.0.0-rc3
  • browserify
App.jsx:
import React from 'react';
import {Router, Route, Link, RouteHandler} from 'react-router';
import Transport from './Transport';
import LeftNav from 'material-ui/lib/left-nav';

let menuItems = [
  { route: 'transport', text: 'Transport' },
  { route: 'customization', text: 'Customization' },
  { route: 'components', text: 'Components' },
];

class AppLeftNav extends React.Component {
    _onLeftNavChange(e, key, payload) {
        console.log(this.context); // empty :(
    }

    render() {
        let header = (<div>Header</div>);
        return (
            <LeftNav ref='leftNav' docked={false} isInitiallyOpen={false} header={header} menuItems={menuItems} onChange={this._onLeftNavChange}/>
        );
    }
}

class AppRouter extends React.Component {

    render() {
        return (
            <Router ref="appRouter" history={history}>
                <Route path='/' component={App}>
                    <Route path='transport' component={Transport}/>
                </Route>
            </Router>
        );
    }
}

class App extends React.Component {
 (...)

    constructor(props, context) {
        super(props, context);
        console.log('constructor', context); // empty :(
    }

    componentDidMount() {
        console.log(this.context); // empty :(
    }

    render() {
        return (
            <div>
                <AppLeftNav ref='appLeftNav' />
                (...)
            </div>
        );
}

App.contextTypes = {
    router: React.PropTypes.func.isRequired
};

export default {App, AppRouter};
main.js:
import React from 'react';
import {AppRouter} from './components/App';
import injectTapEventPlugin from 'react-tap-event-plugin';

injectTapEventPlugin();

React.render((
    React.createElement(AppRouter)
), document.getElementById('app'));

But this.context is always an empty object.

@stebru try:

App.contextTypes = {
  location: React.PropTypes.object
};

@knowbody I then get the location object in this.context.location, but what I'm trying to do is:

_onLeftNavChange(e, key, payload) {
  console.log(this.context);
  this.context.router.transitionTo(payload.route);
}

Is there a transitionToequivalent for the location object?

Please read the Upgrade Guide

Now you use: this.history.pushState(null, '/home')

@knowbody Oh, missed that. Thanks, this works now!

@stebru you're welcome

Same error

  • "react": "^0.14.0",
  • "react-dom": "^0.14.0",
  • "react-router": "^1.0.0-rc3",
  • "babelify": "^6.3.0",
  • "browserify": "^11.2.0",
Uncaught TypeError: Cannot read property 'pushState' of undefined

History is undefined

@cruzlutor yeah, because you didn't npm install history

That is not the reason @knowbody , i got solve it using this guide

https://github.com/rackt/react-router/blob/master/docs/guides/advanced/NavigatingOutsideOfComponents.md

I created a history module and pass it through props to the components that need navigation

@cruzlutor sorry, that's what I assumed from your comment (https://github.com/rackt/react-router/issues/975#issuecomment-149423333).

Glad you worked it out :smile:

I am having trouble getting this to work. I'm trying to get the material ui leftNav to work with react router and redux with nav going through the store and router. I followed both of the examples above but couldn't make it work based on the menuItems.

TypeError: Cannot read property 'isActive' of undefined

I was also referring to these examples:
https://github.com/callemall/material-ui/blob/master/docs/src/app/components/app-left-nav.jsx
http://codetheory.in/react-integrating-routing-to-material-uis-left-nav-or-other-components/

Here is the component I'm working on. I started with react-redux-universal-hot-example. This component is going into another component which is in the Routes.

import React, { Component } from 'react';
import LeftNav from 'material-ui/lib/left-nav';
import RaisedButton from 'material-ui/lib/raised-button';

const menuItems = [
  { route: '/widgets', text: 'Widgets' },
  { route: 'survey', text: 'Survey' },
  { route: 'about', text: 'About' }
];

export default class MaterialLeftNav extends Component {
  static propTypes = {
    history: React.PropTypes.object
  }

  static contextTypes = {
    location: React.PropTypes.object,
    history: React.PropTypes.object
  }

  contructor(props) {
    super(props);
  }

  _onLeftNavChange(e, key, payload) {
    this.props.history.pushState(null, payload.route);
  }

  _handleTouchTap() {
    this.refs.leftNav.toggle();
  }

  _getSelectedIndex() {
    let currentItem;

    for (let i = menuItems.length - 1; i >= 0; i--) {
      currentItem = menuItems[i];
      if (currentItem.route && this.props.history.isActive(currentItem.route)) return i;
    }
  }

  render() {
    return (
      <div>
        <LeftNav
          ref="leftNav"
          docked
          menuItems={menuItems}
          selectedIndex={this._getSelectedIndex()}
          onChange={this._onLeftNavChange}
        />
        <RaisedButton label="Toggle Menu" primary onTouchTap={this._handleTouchTap} />
      </div>
    );
  }

}

For questions and support, please visit our channel on Reactiflux or Stack Overflow. The issue tracker is exclusively for bug reports and feature requests.

ok, sorry about that, I'm new around here

@cruzlutor @knowbody I'm getting the same issue as well: "Cannot read property 'pushState' of undefined"

Mind ELI5 how this fix works? https://github.com/rackt/react-router/blob/master/docs/guides/advanced/NavigatingOutsideOfComponents.md

In short, I feel like it's quering documents that I don't (think) I have? History.js, Index.js, Action.js..

FWIW I'm using a Rails app. Thx in advance.

This is probably more info than you need and may be suspect since my knowledge is limited, but this is how I got it to work.

@TimCannady as we made the issue tracker for bugs and feature request only, can you ping me on Discord? we can follow up there, or just ask the question on the StackOverflow under the react-router tag.

The link to the answer that @ryanflorence posted at the top is dead.

@chrisranderson Although, it seems the advice at the above link was subsequently reversed, and then changed again in react 1.0.0.

So, we now have something like this:

contextTypes: {
  location: React.PropTypes.object,
  history: React.PropTypes.object
},

See an example fix for another component.

I just want to confirm: using this.history.pushState, or this.props.history.pushState, is the correct way of going about redirects?

Please use Stack Overflow or Reactiflux for questions – not the issue tracker.

Is the contextTypes changed to Object recently !
I was trying with

contextTypes: {
  router: React.PropTypes.func.isRequired,
}

Though working, threw warning!

Warning: Failed Context Types: Invalid context router of type object supplied to AddNew, expected function. Check the render method of RouterContext.

Changed to

contextTypes: {
  router: React.PropTypes.object.isRequired,
}

And everything it fine now !

react-router v2.0.0, you can try with:

ComponentName.contextTypes = {
  router: function () {
    return React.PropTypes.func.isRequired;
  }
};

If contextTypes is not defined, then this.context will be an empty object.

I was with react-router 0.13.x for now, so I just cut the BS with
window.location.hash = '/newlocation'

I was using this solution by @Flourad

ComponentName.contextTypes = {
  router: function () {
    return React.PropTypes.func.isRequired;
  }
}

And it worked fine. But then I created a new project and I got this weird error.

Now I'm using this:

MyComponent.contextTypes = {
    router: React.PropTypes.object
}

Which works and makes more sense since router is an object after all, and not a function.

With 3.0 alpha, you can use withRouter() HOC. It will give you router as a prop.
Note that it also exists in 2.x but doesn’t provide router. I strongly suggest switching to 3.x.

It does provider router in 2.x – just not location and params 😄

@taion in the props?

I get, location, params, route, etc, but not the router itself.

With withRouter.

So witRouter() can be used in 2.x ?

Edit: yes, it was introduced in 2.4.0

I think the answer to the original question posted by @tjwebb is that the property "contextTypes" cannot be attached in the class declaration directly when using ES6. ES6 allows methods on the class declaration but not regular properties.
To add the contextTypes property you would have to do this:

class ClassName extends React.Component{
    //... Add all your class method here
}
ClassName.contextTypes = {
    //....Add all your context types here
}

Just want to point to the reference about withRouter. I followed the instructions and got it working by wrapping the component with withRouter and redirecting by calling history.push('/foo') on the component itself.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jzimmek picture jzimmek  Â·  3Comments

winkler1 picture winkler1  Â·  3Comments

nicolashery picture nicolashery  Â·  3Comments

Waquo picture Waquo  Â·  3Comments

davetgreen picture davetgreen  Â·  3Comments