Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing components based on url with react router

This is more of an architectural question regarding react than a specific issue, but what is considered best practice for managing state/props with a layout component and a several child components which are rendered based on the url?

Note: I'm aware that similar questions have been asked, but this is a little bit different. [How to update ReactJS component based on URL / path with React-Router

Lets say I have something like the following code: A profile page (main layout view) with navigation links for profile sub-sections (settings, preferences, account details, etc), and a main panel where each of the sub-section is rendered.

So currently I would have something like this: my router routes.js

<Router history={browserHistory}>
  <Route path='/profile' component={Profile} >
    <IndexRoute component={Summary} />
    <Route path='/profile/settings' component={Settings} />
    <Route path='/profile/account' component={Account} />
    <Route path='/profile/preferences' component={Preferences} />
  </Route>
</Router>

and a stripped down version of my profile layout component profile.js

class Profile extends React.Component {

  constructor(props) {
    super(props)
  }

  render(){

    let pathName = this.props.location.pathname;

    return(
      <div className='container profile-page'>
        <div className='side-nav'>
          <ul>
            <li><Link to='/profile'>Summary</Link></li>
            <li><Link to='/profile/settings'>Settings</Link></li>
            <li><Link to='/profile/account'>My Account</Link></li>
            <li><Link to='/profile/preferences'>Preferences</Link></li>
          </ul>
        </div>
        <div className='main-content'>
         {this.props.children}
        </div>
      </div>
    )
  }
}

export default Profile;

So this kind of works. The child components will render based on the url. But then how do I manage state and props? The way I understand React and Flux, I want the Profile component to manage state and listen to changes on my stores, and to propagate those changes to its children. Is this correct?

My problem is that there doesn't seem to be an intuitive way to pass props to components rendered by this.props.children, which makes me feel like my current architecture and/or understanding of flux is not correct.

A bit of guidance would be much appreciated.

like image 809
jmknoll Avatar asked Jun 24 '16 05:06

jmknoll


1 Answers

I feel what you are doing is perfectly fine. You are on the right path.

There's a combination of APIs that React provides you with that will take care of exactly what you're not certain about of how to achieve ( way to pass props to components rendered by this.props.children )

First, you need to take a look at cloneElement

It will basically take a React element, clone it, and return another with props that you can change, alter or replace entirely based on your needs.

Furthermore, combine it with the Children Utilities - loop through the children that were provided to your top level component and make the necessary changes to each element individually.

A proposed sample usage could be as simple as

<div className='main-content'>
    {React.children.map(this.props.children, (child, index) => {
       //Get props of child
       const childProps = child.props;

       //do whatever else you need, create some new props, change existing ones
       //store them in variables

       return React.cloneElement(child, {
           ...childProps, //these are the old props if you don't want them changed
           ...someNewProps,
           someOldPropOverwritten, //overwrite some old the old props 
       });
     )}
</div>

Use these tools to create truly generic and re-usable components, anywhere. More commonly used utilities from Children are map, forEach and toArray. Each with their own respective goals.

Hope this helps.

like image 174
Elod Szopos Avatar answered Nov 05 '22 10:11

Elod Szopos