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

Props are not passed by Route to components [v4] #4942

Closed
NitroBAY opened this issue Apr 9, 2017 · 22 comments

Comments

10 participants
@NitroBAY
Copy link

commented Apr 9, 2017

Hello,
I use React-route@v4, I have inside my Router :

<Layout searchBar>
    <Route path="/" render={props => <LandingPage {...props} />} />
</Layout>

My layout contains a components which has a component which has a searchBar. I want the content of the searchBar to change the content of a component of LandinPage.
I don't know the best way but I end up with a solution which makes LandingPage having the searchRequest. Now the variable climbed up to the parent component I need to make the variable going down to DrugsList. So my idea was to give to the children component of the layout the prop searchRequest by replacing {children} with :

{React.cloneElement(children, {
    searchRequest,
})}

However <Route path="/" render={props => <LandingPage {...props} />} /> keeps the props for him and doesn't want to give'em to LandingPage so how should I do ?
Actually I'm not only open to understand how making Route passing props to its component but also by finding another way (if you think it's better) to make my components communicating.

@timdorr

This comment has been minimized.

Copy link
Collaborator

commented Apr 9, 2017

You would need to wrap Route in your own component that passes through its props to the render function directly. Basically, how NavLink works: https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/NavLink.js

@timdorr timdorr closed this Apr 9, 2017

@NitroBAY

This comment has been minimized.

Copy link
Author

commented Apr 10, 2017

Well, it worked :

const LandingRoute = ({ searchRequest }) => (
  <Route
    path="/" children={() =>
    <LandingPage searchRequest={searchRequest} />}
  />
);

Meteor.startup(() => {
  render(
    <Router history={history}>
      <Switch>
        <Route path="/">
          <Layout searchBar>
            <LandingRoute />
          </Layout>
        </Route>
      </Switch>
    </Router>, document.getElementById('app'));
});

Why Route doesn't pass props by default though ?

@NitroBAY

This comment has been minimized.

Copy link
Author

commented Apr 10, 2017

I ended up creating a PorousRoute, it's a reusable component which always pass props to its children :

import React from 'react';
import Route from 'react-router';

export default function ({ children, ...props }) {
  return (
    <Route path={props.path}>{React.cloneElement(children, props)}</Route>
  );
}
@skosch

This comment has been minimized.

Copy link

commented Apr 20, 2017

I just ran into a similar issue while migrating to v4 and pulled my hair out for quite a while. The docs clearly state that Route children get props too, so cloneElement should be the default behaviour. Can we re-open this?

Route render methods

There are 3 ways to render something with a <Route>:
<Route component>
<Route render>
<Route children>

Each is useful in different circumstances. You should use only one of these props on a given <Route>. See their explanations below to understand why you have 3 options. Most of the time you’ll use component.

Route props

All three render methods will be passed the same three route props

  • match
  • location
  • history
@jstejada

This comment has been minimized.

Copy link

commented Jul 6, 2017

hi all! just wondering if there any updates on this issue? is this the expected behavior?

@KyleWMiller

This comment has been minimized.

Copy link

commented Jul 26, 2017

I am also having trouble passing props into the render prop of . My confusion comes in where I'm supposed to declare the array of props to be inserted into the router line component.

@JustFly1984

This comment has been minimized.

Copy link

commented Jul 26, 2017

@acomito

This comment has been minimized.

Copy link

commented Jul 29, 2017

Is there no way to do this?

  render(){

    if (this.props.data.loading) {
      return <LoadingScreen />
    }

    return (
      <div className='public-layout'>
          <Switch>
            <Route exact path='/' component={ SignupPage } {...this.props} />
            <Route exact path='/login' component={ LoginPage } {...this.props} />
          </Switch>
      </div>
    );
  }
@skosch

This comment has been minimized.

Copy link

commented Jul 29, 2017

@acomito that's not really the same thing. You can achieve that, for example, by using render={() => <SignupPage {...this.props} />} instead of component={...}. But this issue is about the router-specific props (location etc.) not getting injected automatically.

@acomito

This comment has been minimized.

Copy link

commented Jul 29, 2017

@skosch Thanks for the quick response. That's the answer I needed to track down. Sorry to clog up the wrong thread!

@skosch

This comment has been minimized.

Copy link

commented Jul 29, 2017

(... or even just <Route ...><SignupPage {...this.props /></Route> would work for you too – but again, the problem is that SignupPage wouldn't get access to props.location etc.)

@acomito

This comment has been minimized.

Copy link

commented Jul 29, 2017

i see... so to get props.location we need to do React.clone for each route?

@skosch

This comment has been minimized.

Copy link

commented Jul 29, 2017

Well we shouldn't have to, thus this issue ... 😉

@acomito

This comment has been minimized.

Copy link

commented Jul 29, 2017

True. Yeah one of the pages (SignupPage) has on it.. so I can get my this.props.data there with your fix, but am getting this error still:

Failed context type: The context router.push is marked as required in Link, but its value is undefined.

@KyleWMiller

This comment has been minimized.

Copy link

commented Jul 29, 2017

I found a solution to this issue
<Router path="/somePath" render={() => <SomeComponent someProp={prop} />} />

The docs make things overly complicated. Just think of it as writing a component like you would in any other location.

@KyleWMiller

This comment has been minimized.

Copy link

commented Jul 29, 2017

I'm just using the implicit return, anything more complicated doesn't seem to work

@KyleWMiller

This comment has been minimized.

Copy link

commented Jul 29, 2017

From here you still have access to this.props.someProp inside the child component

@Charlie91

This comment has been minimized.

Copy link

commented Oct 10, 2017

@KyleWMiller, this is the most simple way to pass props, but bad thing is that when i use it i can't see "match.params" property.
So, for me it works great with simple routes, but not with routes like that
<Route exact path="/conceptions/:id" name="Conception" render={() => <Conception wtf={"wtf"} {...this.props} />}/>

@timdorr

This comment has been minimized.

Copy link
Collaborator

commented Oct 10, 2017

@Charlie91

<Route exact path="/conceptions/:id" render={({ match }) => <Conception match={match} {...this.props} />}/>
@Charlie91

This comment has been minimized.

Copy link

commented Oct 10, 2017

@timdorr yeah, it seems i made a mistake i have found it already.
thank you for fixing me!

@brunoandradebr

This comment has been minimized.

Copy link

commented Oct 16, 2017

what a dirt! would be something like this : <Route path="/" component={MyComponent} MyProp={'bla'} /> and a general data share with all routes :

<BrowserRouter MyData={'bla'}>
<Route..../>
<Route..../>
<Route..../>
</ BrowserRouter>

@CraigglesO

This comment has been minimized.

Copy link

commented Oct 17, 2017

I also tried this.

// @flow
import React, { Component } from 'react';
import { observer }         from 'mobx-react';

/** Modules **/
import MainStore from './MainStore';

/** Stylesheets **/
import './App.css';

@observer
class App extends Component {
  mainStore: MainStore;
  constructor() {
    super();
    this.mainStore = new MainStore();
  }

  render() {
    const { children } = this.props;

    return (
      <div className="App">
        {React.cloneElement(children, { api: this.mainStore.api })}
      </div>
    );
  }
}

export default App;

This doesn't work in react-router v4, but DOES work in react-router v3. -_-
I like dom better htough, oh well.... I am adding a comment because if this get's resolved I'll go back to v4

edit -
https://stackoverflow.com/questions/43469071/react-react-router-dom-pass-props-to-component
great explanation here.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
You can’t perform that action at this time.