1

I have difficulties trying to pass props to this.props.children

I have a component MainLayout and I would like to pass props to this.props.children but his children are like that :

AppRouter.js

export default class AppRouter extends Component {

  render() {

    return (
      <HashRouter>
        <Switch>
          <MainLayout>
            <Route exact path="/" component={PubsList} />
            <Route exact path="/add-pub" component={AddPub} />
            <Route exact path="/add-pub/:id" component={AddPub} />
          </MainLayout>
        </Switch>
      </HashRouter>
    )
  }
}

MainLayout.js : I used React.Children and React.CloneElement to pass props

export default class MainLayout extends Component {

 constructor(props) {
  super(props);
  this.state = {
   foo: "foo"
  };

  render() {

    return (
      <div className="container" >
        <Menu inverted color='teal' fixed="top" style={{ margin: "0", padding: "20px", borderRadius: "0", display: "block" }}>    
          <Link to="/">
            <Header as='h2' content='PandaBar' floated="left" style={{ color: "white", margin: "0" }} />
          </Link>    
        </Menu>    

        <div className="content" style={{ marginTop: "70px", overflow: "hidden" }} >    

          {React.Children.map(this.props.children, child => {
           return React.cloneElement(child, { foo: this.state.foo })
          })}    

        </div>
      </div>
    );
  }
}

But, once my routes get the props, it appears like this:

<HashRouter>
   <Switch>
     <MainLayout>
        <Route exact path="/" component={PubsList} foo="foo"/>
        <Route exact path="/add-pub" component={AddPub} foo="foo"/>
        <Route exact path="/add-pub/:id" component={AddPub} foo="foo"/>
     </MainLayout>
    </Switch>
</HashRouter>

So, how can I pass my props foo to my component PubsList and AddPub with this configuration ?

isherwood
  • 58,414
  • 16
  • 114
  • 157
ElTchou
  • 13
  • 2
  • 1
    Can you try using the `render` method as ` } />` – Aruna Sep 27 '18 at 12:58
  • The question was previously dupe-hammered. This question https://stackoverflow.com/questions/22639534/pass-props-to-parent-component-in-react-js is related but addresses a different problem. While its answers can give some ideas, they don't answer this question directly. – Estus Flask Sep 27 '18 at 14:24

2 Answers2

1

Route does provide a way to pass custom props to the component, by using the render prop instead of the component prop. You pass a function in to the render prop, and that function will get called with the same props that are normally automatically passed when using component. You can then do whatever you want to with those props

An example of this (not including your mainLayout component) would look like this:

<Route render={(routeProps) => (
  <PubsList hello="world" {...routeProps} />
)}/>

So using this, you can put arbitrary props onto the components. But then how to let MainLayout dictate what those extra props are? There i think you'll need to modify MainLayout to have a render prop of its own.

render() {
  let children = null;
  if (this.props.render) {
    children = this.props.render({ foo: this.state.foo })
  }

  return (
    <div className="container" >
      <Menu inverted color='teal' fixed="top" style={{ margin: "0", padding: "20px", borderRadius: "0", display: "block" }}>    
        <Link to="/">
          <Header as='h2' content='PandaBar' floated="left" style={{ color: "white", margin: "0" }} />
        </Link>    
      </Menu>    

      <div className="content" style={{ marginTop: "70px", overflow: "hidden" }} >    
        {children}
      </div>
    </div>
  );
}

And you would use it like this:

<MainLayout render={(layoutProps) => (
  <React.Fragment>
    <Route exact path="/" render={(routeProps) => 
      <PubsList {...layoutProps} {...routeProps} />
    }/>
    <Route exact path="/add-pub" render={(routeProps) => 
      <AddPub {...layoutProps} {...routeProps} />
    }/>
    <Route exact path="/add-pub/:id" render={(routeProps) => 
      <AddPub {...layoutProps} {...routeProps} />
    }/>
  </React.Fragment>
)} />
Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98
1

In the example foo prop is passed to wrong component (Route).

In order to be passed to route components, route component prop should be changed instead:

const withFoo = (Comp, foo) => props => <Comp {...props} foo={foo}/>;
...

  render() {

    return (
      ...
      {React.Children.map(this.props.children, route => {
        return React.cloneElement(route, {
          component: withFoo(route.props.component, this.state.foo)
        });
      })}    
      ...
    );
  }

While the use of Route render prop as another answer suggest may be more straightforward.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • Thanks you, it really helps ! – ElTchou Sep 27 '18 at 13:45
  • But just one another question, what means "=> props =>" ? I understand it is use to get the props from the original component but how it is written when this function is not simplified ? Thanks a lot – ElTchou Sep 27 '18 at 13:49
  • You're welcome. It's higher-order component, https://reactjs.org/docs/higher-order-components.html . Where `props => ...` is functional component, https://reactjs.org/docs/components-and-props.html#functional-and-class-components . `withFoo` creates a wrapper for route component that works like it but additionally provides `foo` prop. – Estus Flask Sep 27 '18 at 14:02