17

I am trying to figure out the proper "react" way to pass in an optional prop that is an Element to a container component, that is handled differently from the children of that component.

For a simple example, I have a Panel component, which renders its children, that also has an optional "title" prop (which is an element rather than a string, for the sake of the example) that gets specially rendered (put in a special spot, with special behaviors in while maintaining the abstraction.

One option is to have a component which is pulled out of the children and rendered specially:

<Panel>
   <Title> some stuff</Title>
   <div> some other stuff</div>
</Panel>

But it seems wierd to have the children pulled out and handled separately like that.

How is this normally handled in react, and am I even thinking about this the right way

Zak Kus
  • 1,503
  • 3
  • 15
  • 28

4 Answers4

16

You don't need to do anything special. Just pass the title component as a prop, and then use {this.props.title} wherever you want it to be rendered:

class Panel extends React.Component {
  render() {
    return <div>
      {this.props.title}
      <div>Some other stuff...</div>
    </div>;
  }
}

class App extends React.Component {
  render() {
    var title = <Title>My Title</Title>;
    return <Panel title={title}/>;
  }
}

If you don't pass any value for the title prop (or if the value is false, null, or undefined) then nothing will be rendered there.

This is a fairly common pattern in React.

Jordan Running
  • 102,619
  • 17
  • 182
  • 182
  • 1
    ugh, thanks. I was stuck in this view that "title={title}" had to be a primitive type. – Zak Kus Nov 10 '15 at 22:02
  • Is it a common react pattern (or even possible) to restrict the title propType to be an actual Title component? – Zak Kus Nov 10 '15 at 23:29
  • It's correct in general, but there is exceptions. If the prop contain array that doesn't exist, it will does generate error (that's what I have got in my tests anyway). This : ** {this.props.data.contactDetails [1]}** will generate error if : this.props.data.contactDetails [1] didn't provided. – lingar May 11 '20 at 08:04
5

you can do something like this

render(){
    <div>
        {this.props.title ? this.props.title : null}
        {this.props.children}
    </div>
}

basically if you pass a title element as a prop then create it as an element and render it. else just put in null...

to create it you would do something like this.

<Panel title={<Title>Something Here</Title>}
    <div> something here</div>
</Panel>

This is generally how react should handle optional child components

John Ruddell
  • 25,283
  • 6
  • 57
  • 86
1

When you need an attribute from your optional prop, you will have to check first if the prop was delivered. Otherwise, you will get a:

TypeError: Cannot read property 'yourPropProperty' of undefined

In conditional rendering context (depending on my optional this.props.ignore array), this won't work:

{!this.props.ignore.includes('div')) && (
   <div>
      Hey
   </div>
)}

Instead, you should do:

{(!this.props.ignore || !this.props.ignore.includes('div'))) && (
   <div>
      Hey
   </div>
)}
MBT
  • 21,733
  • 19
  • 84
  • 102
0

One thing you can do is have default props (usually initialised to a no-op) for your component.

For example, if you want to have an optional function prop:

class NewComponent extends React.Component {
    ...
    componentDidMount() {
        this.props.functionThatDoesSomething()
    }
}

NewComponent.defaultProps = {
    functionThatDoesSomething: () => {}
}

This way, parent components can choose to pass the function prop or not and your app won't crash due to the error

this.props.functionThatDoesSomething is not a function .

Taiwosam
  • 469
  • 7
  • 13