Rob
Rob

Reputation: 7217

ReactJs - how to use multiple children in a component

I'm prefacing this with the fact I'm VERY new to ReactJs and probably trying to work out something quite basic.

I have this code with a custom piece of HTML:

Sample Component

const Sample = ({ title, children }) => {
    return (
          <div class="panel">
              <div class="title">
                 {title}
              </div>
              <div class="body">
                 {children}
              </div>
           </div>    
     );
};

Side question - whats the correct name for the above? A fragment? It doesn't look to be a "component" but just learning the naming conventions for React too

Utilise Component

export default class ExampleComponent extends Component {
    render() {
        return <div class="page-body">
            <div class="row">
                <h1> Page 1 </h1>
                <Sample title={<h1>Some title!</h1>}>
                   <p>This is my sample body!</p>
                </Sample>
            </div>
        </div>
    }
}


You can see that the content of the "Sample" element is taken automatically as children property but to set title I have to explicitly set "title" property. What I'd ideally like to do is something similar to the below:

Desired way to utilise Sample Component

export default class ExampleComponent extends Component {
    render() {
        return <div class="page-body">
            <div class="row">
                <h1> Page 1 </h1>
                <Sample title="Some title!">
                   <Sample.Title>
                       <h1>This is my new way to do a title</h1>
                   </Sample.Title>
                   <Sample.Body>
                       <p>This is my sample body!</p>
                   </Sample.Body>
                </Sample>
            </div>
        </div>
    }
}

I've used this type of approach before with components others have created but want to do it myself now and finding it hard to get a simple example to do this.

Thanks in advance for any pointers!

Upvotes: 16

Views: 34640

Answers (3)

hackape
hackape

Reputation: 19947

The Children utils suit can be helpful for your scenario.

import { Children } from 'react'

const Sample = ({ title, children }) => {
  let _body, _title

  Children.forEach(children, child => {
    if (child.type === SampleTitle) {
      return _title = child
    }

    if (child.type === SampleBody) {
      return _body = child
    }
  })

  if (!_title) _title = <div className='title'>{title}</div>
  if (!_body) _body = <div className='title'>{children}</div>

  return (
    <div className='panel'>
      {_title}
      {_body}
    </div>
  )
}

const SampleTitle = ({ children }) => <div className='title'>{children}</div>
const SampleBody = ({ children }) => <div className='body'>{children}</div>

Sampe.Title = SampleTitle
Sample.Body = SampleBody

Now you can use Sample in multiple ways:

<Sample title="my title">
  <div>my body</div>
</Sample>

<Sample title="my title">
  <Sample.Body>my body</Sample.Body>
</Sample>

<Sample title="my fallback title">
  <Sample.Title>my overriding title</Sample.Title>
  <Sample.Body>my body</Sample.Body>
</Sample>

Upvotes: 16

Kobe
Kobe

Reputation: 6446

I think what you want is called JSX Namespacing? Either way, you can instantiate Sample, and then add more components as properties of Sample (view it as an object):

import React from "react"

const Sample = ({ children }) => (
  <div className="panel">
      {children}
  </div>    
)


Sample.Title = (props) => <div className="title">{props.children}</div>
Sample.Body = (props) => <div className="body">{props.children}</div>

export default Sample

Note: React uses className rather than class since we are using JSX.

Upvotes: 24

trixn
trixn

Reputation: 16309

In that case you just have to extract the relevant containers into their own components:

const Sample = ({children }) => (
    <div className="panel">{children}</div>    
);

const Title = ({children}) => (
    <div className="title">{children}</div>
);

const Body = ({children}) => (
    <div className="body">{children}</div>
);

Sample.Title = Title;
Sample.Body = Body;

Also note that the correct prop for a css class is className.

Upvotes: 3

Related Questions