Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

this.context.router is undefined #975

Closed
tjwebb opened this issue Mar 21, 2015 · 94 comments
Closed

this.context.router is undefined #975

tjwebb opened this issue Mar 21, 2015 · 94 comments

Comments

@tjwebb
Copy link

@tjwebb tjwebb commented Mar 21, 2015

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 💃

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

@agundermann
Copy link
Contributor

@agundermann agundermann commented Mar 21, 2015

Your class also needs

  contextTypes: {
    router: React.PropTypes.func.isRequired
  },
@tjwebb
Copy link
Author

@tjwebb tjwebb commented Mar 21, 2015

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

@svenanders
Copy link

@svenanders svenanders commented Mar 21, 2015

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)

@tjwebb
Copy link
Author

@tjwebb tjwebb commented Mar 21, 2015

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.

@agundermann
Copy link
Contributor

@agundermann agundermann commented Mar 21, 2015

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.

@svenanders
Copy link

@svenanders svenanders commented Mar 21, 2015

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.

@tjwebb
Copy link
Author

@tjwebb tjwebb commented Mar 21, 2015

I updated the examples.

Where are these fiddles?

@agundermann
Copy link
Contributor

@agundermann agundermann commented Mar 21, 2015

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

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

@alexkirsz
Copy link

@alexkirsz alexkirsz commented Mar 21, 2015

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}/>;
    }
  });
}
@ryanflorence
Copy link
Member

@ryanflorence ryanflorence commented Mar 21, 2015

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?

@pscanf
Copy link

@pscanf pscanf commented Mar 21, 2015

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 😉).

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.

@MariusRumpf
Copy link

@MariusRumpf MariusRumpf commented Mar 21, 2015

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;
@olegsmetanin
Copy link

@olegsmetanin olegsmetanin commented Mar 21, 2015

MariusRumpf +100500

@akinnee
Copy link

@akinnee akinnee commented Mar 22, 2015

My context object is also empty on 0.13.1.

@tylermcginnis
Copy link
Contributor

@tylermcginnis tylermcginnis commented Mar 22, 2015

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

@sgehly
Copy link

@sgehly sgehly commented Mar 22, 2015

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

@tjwebb
Copy link
Author

@tjwebb tjwebb commented Mar 22, 2015

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?

@olegsmetanin
Copy link

@olegsmetanin olegsmetanin commented Mar 22, 2015

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();
    });

  });

});
@MariusRumpf
Copy link

@MariusRumpf MariusRumpf commented Mar 22, 2015

@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.

@n1ghtmare
Copy link

@n1ghtmare n1ghtmare commented Mar 23, 2015

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.

@tjwebb
Copy link
Author

@tjwebb tjwebb commented Mar 23, 2015

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.

@deser
Copy link

@deser deser commented Mar 23, 2015

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) ?

@gaearon
Copy link
Contributor

@gaearon gaearon commented Mar 23, 2015

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

No.

Have you tried adding contextTypes as suggested above?

@n1ghtmare
Copy link

@n1ghtmare n1ghtmare commented Mar 23, 2015

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

@gaearon
Copy link
Contributor

@gaearon gaearon commented Mar 23, 2015

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

@n1ghtmare
Copy link

@n1ghtmare n1ghtmare commented Mar 23, 2015

@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

@gaearon
Copy link
Contributor

@gaearon gaearon commented Mar 23, 2015

Oh. New version isn't on CDN yet.

@n1ghtmare
Copy link

@n1ghtmare n1ghtmare commented Mar 23, 2015

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

@gaearon
Copy link
Contributor

@gaearon gaearon commented Mar 23, 2015

Just added this to the docs: c17ef27

@deser
Copy link

@deser deser commented Mar 23, 2015

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

@cruzlutor
Copy link

@cruzlutor cruzlutor commented Oct 20, 2015

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

@knowbody
Copy link
Contributor

@knowbody knowbody commented Oct 20, 2015

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

@cruzlutor
Copy link

@cruzlutor cruzlutor commented Oct 22, 2015

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

@knowbody
Copy link
Contributor

@knowbody knowbody commented Oct 22, 2015

@cruzlutor sorry, that's what I assumed from your comment (#975 (comment)).

Glad you worked it out 😄

@CalebEverett
Copy link

@CalebEverett CalebEverett commented Oct 30, 2015

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>
    );
  }

}
@knowbody
Copy link
Contributor

@knowbody knowbody commented Oct 31, 2015

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

@CalebEverett
Copy link

@CalebEverett CalebEverett commented Oct 31, 2015

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

@TimCannady
Copy link

@TimCannady TimCannady commented Nov 5, 2015

@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.

@CalebEverett
Copy link

@CalebEverett CalebEverett commented Nov 5, 2015

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.

@knowbody
Copy link
Contributor

@knowbody knowbody commented Nov 6, 2015

@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.

@chrisranderson
Copy link

@chrisranderson chrisranderson commented Nov 12, 2015

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

@adamcharnock
Copy link

@adamcharnock adamcharnock commented Nov 16, 2015

@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.

@backnotprop
Copy link

@backnotprop backnotprop commented Jan 6, 2016

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

@taion
Copy link
Contributor

@taion taion commented Jan 6, 2016

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

@saumya
Copy link

@saumya saumya commented Mar 4, 2016

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 !

@Flourad
Copy link

@Flourad Flourad commented Mar 17, 2016

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.

@jkarttunen
Copy link

@jkarttunen jkarttunen commented Mar 25, 2016

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

@PierBover
Copy link

@PierBover PierBover commented Jul 2, 2016

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.

@gaearon
Copy link
Contributor

@gaearon gaearon commented Jul 2, 2016

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.

@taion
Copy link
Contributor

@taion taion commented Jul 3, 2016

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

@PierBover
Copy link

@PierBover PierBover commented Jul 3, 2016

@taion in the props?

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

@taion
Copy link
Contributor

@taion taion commented Jul 3, 2016

With withRouter.

@PierBover
Copy link

@PierBover PierBover commented Jul 3, 2016

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

Edit: yes, it was introduced in 2.4.0

@buchanora
Copy link

@buchanora buchanora commented Nov 19, 2016

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
}
@automata
Copy link

@automata automata commented Apr 27, 2017

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.

@ReactTraining ReactTraining deleted a comment from kiransiluveru Aug 2, 2018
@lock lock bot locked as resolved and limited conversation to collaborators Oct 1, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet