score:23

Accepted answer

Make seperate components Donot use props to pass components as children. something like this.

header.js

import React from 'react';
import styles from "./index.css";

export default React.createClass({
  getComponent(key) {
     return this.props.children.filter( (comp) => {
             return comp.key === key;
     });
  }
  render: function() {
    return (
      <header className={styles.root}>
        <div className={styles.logo}>
          {this.getComponent('logo')}
        </div>
        <div>
         {this.getComponent('navbar'}
    
        </div>
      </header>
    );
  }
});

app.js

export default React.createClass({
      render: function() {
        return (
          <Header>
            <Logo key="logo"/>
            <Navbar key="navbar"/>
          </Header>
        );
      }
    });

score:-1

That question inspired me. Here is my approach:

First child component: TestA.tsx

import * as React from 'react';

export default class TestA extends React.Component {
    render () {
        return (
            <div className="TestA">{this.props.children}</div>
        )
    }
}

Second child component: TestB.tsx

import * as React from 'react';

export default class TestB extends React.Component {
    render () {
        return (
            <div className="TestB">{this.props.children}</div>
        )
    }
}

Parent component: TestAB.tsx

import * as React from 'react';

type TestABProps = {
    children: React.ReactNode;
}

export default class TestAB extends React.Component<TestABProps> {

    //Loop through React nodes in children and get all instances of the specified component
    getComp( comp: string ): null | React.ReactNode | React.ReactNode[] {
        if( this.props.children instanceof Array ) {
            let nodes: React.ReactNode[] = [];
            for( let child of this.props.children ) {
                if( ((child as React.ReactElement)?.type as Function)?.name == comp ) {
                    nodes.push( child );
                }
            }
            return nodes.length > 1 ? nodes : nodes[0];
        }
        return null;
    }
    render () {
        return (
            <div className="AB">
                <div className ="A">
                    {this.getComp("TestA")}
                </div>
                <div>This is a DIV between the components</div>
                <div className ="B">
                    {this.getComp("TestB")}
                </div>
            </div>
        )
    }
}

As you can see, you only have to place {this.getComp("YourComponentName")} where you want all the instances of this component to appear in your parent component.

Usage example

<TestAB>
    <TestA>Foo</TestA>
    <TestB>Bar</TestB>
    <TestA>Foobar</TestA>
</TestAB>

Usage is very simple and intuitive. No need for keys or anything else, just insert your child components without weird syntax.

Resulting HTML

<div class="AB">
    <div class="A">
        <div class="TestA">Foo</div>
        <div class="TestA">Foobar</div>
    </div>
    <div>This is a DIV between the components</div>
    <div class="B">
        <div class="TestB">Bar</div>
    </div>
</div>

Note that you can insert the children of TestAB in any order - they will be inserted in the right place.

All this can certainly be improved, you can adapt this to your needs. For example, if you put any child component other than TestA or TestB, they won't be displayed, that would be my first improvement.

score:2

You can treat props as children, because technically children is just another prop. There's nothing wrong with this approach – React contributors themselves suggest you do that (Support multiple child insertion points).

In addition, if you're working in a big team of engineers, I would also suggest standardizing how you name those props as well as their standard behavior – are they just plain content or a callback you can pass arguments to. react-placeholders can help you with that. So, the code of your component can look like that:

// header.jsx
import React from 'react';
import withSlots from 'react-placeholders';

@withSlots('logo', 'navigation')
export default class Header extends React.Component {
  render() {
    return (
      <header className={styles.root}>
        <div className={styles.logo}>
          {this.props.header()}
        </div>
        <div>
          {this.props.navigation('home')}
        </div>
      </header>
    );
  }
}

And this is how you'd embed that component

// app.jsx
import Header from 'header.jsx'

export default class Header extends React.Component {
  render() {
    return (
      <Header
        logoSlot={<img src="logo.svg" />}
        navigationSlot={(currentPage) => { 
          return <a href="#" style={{ color: currentPage ? 'blue' : 'red' }}>Home</a>
        }}
      />
    );
  }
}

score:36

import React, { Component } from 'react';
import styles from "./index.css";
import Logo from "./components/Logo";
import Navbar from "./components/Logo";

class Comp extends Component {
    render(){
        return (
            <Header className={styles.root}
                top={Logo}
                right={Navbar}
                />
        );
    }
}

class  Header extends Component {
    render(){
        let {top,right} =this.props;
        return (
            <header className={styles.root}>
                <div className={styles.logo}>
                    {top && <top />}
                </div>
                <div>
                    {right && <right />}
                </div>
            </header>
        );
    }
}

Related Query

More Query from same tag