2

I'm kind of new to reactjs and I'm learning step by step. I'm facing a problem and that is when I try to access the location parameter in the props it returns me undefined. I tried to find the solution but most of the people have written I have to add my component in the router but I'm wrapping it in the router but still, I don't have access to location parameter

export const abcdContainer = withRouter(connect(
    mapStateToProps,
    mapDispatchToProps
)(abcd));

I'm trying to access the location parameter in the and component but the problem is there is no location parameter in it. Can somebody tell me what is it that is going wrong

Please if anybody know what is wrong please tell me I have spent half of my day and I can't figure it out

CODE AND VERSION ADDED WITH URL

Router version => 2.8.1

URL : http://localhost:3000/somePage?someParam=cm9oYW5qYWxpbHRlYWNoZXJAZ21haWwuY29t

abcdContainer.js

const mapStateToProps = (state, ownProps) => {
   // some code over here
}

const mapDispatchToProps = (dispatch, ownProps) => {
           // some code over here
};

export const abcdContainer = withRouter(connect(
    mapStateToProps,
    mapDispatchToProps
)(abcd));

abcd.jsx

class abcd extends Component {

    constructor(props, context) {
        super(props, context);
        this.state = {
           // setting state over here
        };
    }

    abcdFunction(){
        if (this.props.location.query.someParam !== undefined){ // trying to extract someParam value  from location
            // some code over here
        }
    }

    render() {
        // some code over here   
    }
}


export default CSSModules(abcd, styles, { allowMultiple: true });

Here is the flow. The router redirect to the container and then the container redirect to the real component

Route.js

export const Routes = ({ store }) => (
    <Provider store={store}>
        <Router history={browserHistory}>
            <Route path="/" component={aContainer}>
                <IndexRoute component={IContainer} />
                // some routes    
                <Route path="/openAbcd" component={() => (<abcdContainer caller="aaaaaaa" />)} />
               // some routes
            </Route>

           // some routes
        </Router>
    </Provider>
);

Routes.propTypes = {
    store: React.PropTypes.object.isRequired,
};
trixn
  • 15,761
  • 2
  • 38
  • 55
Gardezi
  • 2,692
  • 2
  • 32
  • 62
  • You have to provide a more complete example otherwise it will be hard tell. Post a minimal but complete example of your code including the component. And which version of react-router are you using? – trixn Jul 02 '17 at 01:39
  • @trixn I have added the code amd the version – Gardezi Jul 02 '17 at 01:49
  • are you expecting the location object to be the one in window or is the naming just similar? In any case its not in props because there is no parent component passing it into the child. – terpinmd Jul 02 '17 at 02:31
  • @trixn I was reading somewhere that when we put it in `withRouter` automatically the location is introduced in our props – Gardezi Jul 02 '17 at 04:57
  • remember that the location is only passed to the component that you have settled as a router! and is not nested pass, so if you want the location in an inner component you have to pass it as a prop – Jose Paredes Jul 02 '17 at 05:03
  • @JoseAPL that's not completely right. `withRouter()` uses the [react context](https://facebook.github.io/react/docs/context.html) to pass the router props to components without the need to pass them all the way down via props. – trixn Jul 02 '17 at 05:14
  • @Gardezi Nontheless you need a `` component somewhere up your render tree. Can you post the code where you defined your `` component and your routes? If you don't have a `` somewhere up your component tree it can't be in your react context. Also you should consider updating to a newer version of `react-router` since there is already v4 out. – trixn Jul 02 '17 at 05:20
  • @trixn I have added the router code – Gardezi Jul 02 '17 at 05:29
  • @trixn as Facebook says **If you want your application to be stable, don't use context. It is an experimental API and it is likely to break in future releases of React.**, that's why I didn't mention it – Jose Paredes Jul 02 '17 at 10:21
  • @JoseAPL In general yeah i would agree, but he already uses redux in his project which also makes [heavy usage](http://redux.js.org/docs/basics/UsageWithReact.html#passing-the-store) of the the context. [Also read Dan Abramov's opoinion about this](https://stackoverflow.com/a/36431583/5005177) who works for the react team at facebook and is the main creator of redux. As `react-router` is an essantial part of react and well maintained, it should be okay to trust the maintainers to fix any upcoming api changes. – trixn Jul 02 '17 at 11:22
  • @trixn I know Redux uses the context, but as Dam mention **The important part here is the word libraries. If context changes its behavior, we as library authors will need to adjust** that's why developers that implement libraries like `redux` avoid working with the context, and also that's the main goal to decouple the developer from the context and let the libraries to adjust to the changes, furthermore using `redux` and `context` creates inconsistency in your project – Jose Paredes Jul 02 '17 at 11:37
  • @JoseAPL Why didn't you quote this part of his comment on this? `In some cases its conveniences outweigh its downsides so some libraries like React Redux and` **`React Router`** `choose to rely on it despite the experimental nature.` Isn't react-router a library for you? It has **20.000 downloads per day at npm** and the last commit was 2 days ago. – trixn Jul 02 '17 at 11:43
  • @trixn It is, and also uses the context! but yes I guess all about is conventions. – Jose Paredes Jul 02 '17 at 11:52
  • @JoseAPL It's more about trust. Facebook uses the context api themself in serveral of their apps so it's unlikely that they will drop this feature. Redux and React-Router are two main and well maintained libraries with thousands of downloads a day. They will adapt to changes to the context api pretty quickly. I agree with you if it is a small unknown library that barely get's updated. – trixn Jul 02 '17 at 12:48

1 Answers1

2
<Route path="/openAbcd" component={() => (<abcdContainer caller="aaaaaaa" />)} />

If you use an inline arrow function to render your component why you don't just pass the props directly to the component? Then you will not need withRouter(). Like this:

<Route path="/openAbcd" component={props => (<abcdContainer caller="aaaaaaa" {...props} />)} />

Also the docs of react-router v2.8.1 says about withRouter():

A HoC (higher-order component) that wraps another component to provide props.router.

It doesn't provide location but router as a prop. I recommend you to update react-router to v4 or at least v3.

EDIT: "But why were the props not being inserted implicitly?":

React knows two types of components. Stateless functional components and class-based components. Functional components are functions that accept a single props object argument with data and return a React element. Take a look at this line of your code again:

<Route path="/openAbcd" component={() => (<abcdContainer caller="aaaaaaa" />)} />

You passed an arrow function () => (<abcdContainer caller="aaaaaaa" />) to your <Route> element which is an inline definition of a functional component that takes props as a parameter and returns a rendered React element, in this case this is your <abcdContainer>. But as you can see you omitted the props parameter in your function by defining it with empty parenthesis: () => (<AComponent />). React does not automatically pass props to child components that are rendered inside a functional component. When wrapping your <abcdContainer> into an inline functional component you are responsible for passing props to it yourself.

But if you pass the class/variable of your class-based/functional component to the component prop of your <Route> element like you did it in your other routes then it will render this component an implicitly pass props to it because it isn't wrapped in a functional component:

// this will directly render <aContainer> and pass the props to it
<Route path="/" component={aContainer}>

What you did is creating a "functional unnamed wrapper component" that doesn't take any props and also doesn't pass any props further down.

And note that you should not use withRouter() extensively. This HOC is only there to inject a router into components that do not get rendered by a matching route itself and are e.g. much deeper down your component tree. In your case you do not need it.

trixn
  • 15,761
  • 2
  • 38
  • 55
  • Trixn this helped but why were the props not being inserted implicitly. And if you can share some link which could help me in understanding that would be great help :) – Gardezi Jul 02 '17 at 13:50
  • @Gardezi see my updated answer. If it solved your problem please mark it as the accepted answer. – trixn Jul 02 '17 at 14:54