GreenAsJade
GreenAsJade

Reputation: 14685

How to efficiently pass the same "prop" to every stateless presentation component?

I have what looks like it will be a simple and common case of non-DRY code - I'm hoping there's a way to avoid this.

I have a page to render information about an object based on a route like this:

  <Route path="project/:projectid" component={ProjectDetails}/>

So... the 'project id' comes in as a prop from React Router, and every stateless presentation component needs the resulting project object:

class ProjectDetails = ({project}) => {
   render() {
      return (
         <div className="project-details-page container-fluid">
             <HomeLeftBar/> 
             <div className="col-md-10">
                 <ProjectHeaderBar project={project}/>
             </div>
             <div className="project-centre-pane col-md-7">
                 <ProjectActions project={project}/>
                 <ProjectDescription project={project}/>
                 <ProjectVariations project={project}/>
             </div>
             <div className="project-right-bar col-md-3">
                 <ProjectContacts project={project}/>
                 <ProjectCosting project={project}/>
             </div>
         </div>
      )
   }
}

export default connect(
   state => ({project: state.projects[state.router.params.projectid]})
)(ProjectDetails)

How should I clean this up, so I don't have to pass the project to every component?


I've noted a couple of similar questions this and this at least. But while their titles are similar, their details are not. They seem to ask more advanced questions about more specific situations.

Upvotes: 2

Views: 88

Answers (2)

ArneHugo
ArneHugo

Reputation: 6509

I assume you are using react-router. You can do something like this:

        <Router history={history}>
            <Route path="project/:projectId">
                <Route path="participants" component={Participants} />
                <Route path="progress" component={Progress}/>
            </Route>
        </Router>

All children of <Route path="project/:projectId"> have this.props.params.projectId available. For example, url to the Participants component is "project/:projectId/participants", and therefore projectId is a param when rendering Participants.

Also, if the project details should be rendered the same way on all pages, you can add a component on the Route to "project/:projectId", which is responsible for doing rendering the project details. (That component would receive Participants or Progress as this.props.children, which you'd have to render in the new component.)

Upvotes: 1

Jamie Dixon
Jamie Dixon

Reputation: 54021

For me, passing the data down from the top via props is the clearest way to get data into your leaf components, so I think what you've done here is fine.

The bit that looks not-clean is that you're passing the whole project object to every component but at a glance, it looks as though each of those components is responsible for rendering only a portion of that project object.

Passing only the branches of project that each component needs will make the relationship between the data and the view much clearer.

<ProjectContacts project={project.contracts}/> <ProjectCosting project={project.costing}/>

and in turn I think this will make the whole component feel cleaner:

class ProjectDetails = ({project: { costings, description, variations, title, actions, contacts} }) => {
   render() {
      return (
         <div className="project-details-page container-fluid">
             <HomeLeftBar/> 
             <div className="col-md-10">
                 <ProjectHeaderBar title={title}/>
             </div>
             <div className="project-centre-pane col-md-7">
                 <ProjectActions actions={actions}/>
                 <ProjectDescription description={description}/>
                 <ProjectVariations variations={variations}/>
             </div>
             <div className="project-right-bar col-md-3">
                 <ProjectContacts contacts={contacts}/>
                 <ProjectCosting costings={costings}/>
             </div>
         </div>
      )
   }
}

Upvotes: 3

Related Questions