score:33

Accepted answer

This question boils down to, how do you pass props to children?

June 2018 answer

Today's tech:


Assuming some stateful component:

import React from 'react'
import { BrowserRouter, Route } from 'react-router-dom'

// some component you made
import Title from './Title'

class App extends React.Component {
  // this.state
  state = { title: 'foo' }

  // this.render
  render() {
    return (
      <BrowserRouter>

        // when the url is `/test` run this Route's render function:
        <Route path="/:foobar" render={

          // argument is props passed from `<Route /`>
          routeProps => 

            // render Title component
            <Title 
              // pass this.state values
              title={this.state.title}

              // pass routeProps values (url stuff)
              page={routeProps.match.params.foobar} // "test"
            />

        } />

      </BrowserRouter>
    )
  }
}

This works because this.props.children is a function:

// "smart" component aka "container"
class App extends React.Component {
  state = { foo: 'bar' }
  render() {
    return this.props.children(this.state.foo)
  }
}

// "dumb" component aka "presentational"
const Title = () => (
  <App>
    {title => <h1>{title}</h1>}
  </App>
)

Example on codesandbox

My previous oldschool answer that I wouldn't recommend anymore:

Using a couple of React helper methods you can add state, props and whatever else to this.props.children

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

  return <div>{children}</div>
}

Then your child component can access this via props, this.props.foo.

score:2

There's also the option of using Context. React-Router relies on it to give access to the Router object in the route components.

From another answer I gave on a similar question:

I quickly put together an example using contexts on codepen. MainLayout defines some properties that could be used by the children using the context: users and widgets. These properties are used by the UserList and WidgetList components. Notice they need to define what they need to access from the context in the contextTypes object.

var { Router, Route, IndexRoute, Link } = ReactRouter

var MainLayout = React.createClass({
  childContextTypes: {
    users: React.PropTypes.array,
    widgets: React.PropTypes.array,
  },
  getChildContext: function() {
    return {
      users: ["Dan", "Ryan", "Michael"], 
      widgets: ["Widget 1", "Widget 2", "Widget 3"]
    };
  },
  render: function() {
    return (
      <div className="app">
        <header className="primary-header"></header>
        <aside className="primary-aside">
          <ul>
            <li><Link to="/">Home</Link></li>
            <li><Link to="/users">Users</Link></li>
            <li><Link to="/widgets">Widgets</Link></li>
          </ul>
        </aside>
        <main>
          {this.props.children}
        </main>
      </div>
      )
  }
})

var Home = React.createClass({
  render: function() {
    return (<h1>Home Page</h1>)
  }
})

var SearchLayout = React.createClass({
  render: function() {
    return (
      <div className="search">
        <header className="search-header"></header>
        <div className="results">
          {this.props.children}
        </div>
        <div className="search-footer pagination"></div>
      </div>
      )
  }
})

var UserList = React.createClass({
  contextTypes: {
    users: React.PropTypes.array
  },
  render: function() {
    return (
      <ul className="user-list">
        {this.context.users.map(function(user, index) {
          return <li key={index}>{user}</li>;  
        })}
      </ul>
      )
  }
})

var WidgetList = React.createClass({
  contextTypes: {
    widgets: React.PropTypes.array
  },
  render: function() {
    return (
      <ul className="widget-list">
        {this.context.widgets.map(function(widget, index) {
          return <li key={index}>{widget}</li>;  
        })}
      </ul>
      )
  }
})

var Routes = React.createClass({
  render: function() {
    return <Router>
        <Route path="/" component={MainLayout}>
          <IndexRoute component={Home} />
          <Route component={SearchLayout}>
            <Route path="users" component={UserList} />
            <Route path="widgets" component={WidgetList} />
          </Route> 
        </Route>
      </Router>;
  }
})

ReactDOM.render(<Routes/>, document.getElementById('root'))

score:11

You can use the React method "cloneElement" to accomplish this. When you clone the element, you can pass in props at that time. Use the clone instead of the original in your render fn. eg:

    render: function() {
    var childrenWithProps = React.cloneElement(this.props.children, {someProp: this.state.someProp});
    return (
      <div>
        <Header />
        {childrenWithProps}
      </div>
    );
  }

Related Query

More Query from same tag