16

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!

2
  • 1
    The above is a component. A fragment is <>/* content */</> Commented Nov 11, 2019 at 9:25
  • 1
    In your desired output, are you sure you want to pass through title="Some Title!"? That seems to defeat the purpose of the following code ;) Commented Nov 11, 2019 at 9:28

3 Answers 3

25

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.

Sign up to request clarification or add additional context in comments.

1 Comment

@RobMcCabe No worries. If you ever need more help with react or don't understand something, feel free to ask 👍
17

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>

Comments

3

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.