反应组件可以有多个儿童内容区域吗?

时间:2016-06-26 13:15:11

标签: javascript reactjs

我们说我有一个简单的反应组件:

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

export default React.createClass({
  render: function() {
    return (
      <header className={styles.root}>
        // area for child content
        {this.props.children}
      </header>
    );
  }
});

现在让我们说代替任何子组件的一个区域,我想要两个,就像这样:

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

export default React.createClass({
  render: function() {
    return (
      <header className={styles.root}>
        <div className={styles.logo}>
          // logo children here
        </div>
        <div>
          // navigation children here
        </div>
      </header>
    );
  }
});

我知道我可以使用属性,但对于大量的html内容来说,这不会很优雅。如何通过类似于例如swig中named blocks的方式做出反应?

命名块的示例:

{% extends 'layout.html' %}

{% block title %}My Page{% endblock %}

{% block head %}
  {% parent %}
  <link rel="stylesheet" href="custom.css">
{% endblock %}

{% block content %}
<p>This is just an awesome page.</p>
{% endblock %}

您可以通过参考他们的名字来使用这些块,从而允许这些块产生&#39;多个领域的内容。我希望有一种同样优雅的方式来做到这一点,因为它使组件非常易于组合。

4 个答案:

答案 0 :(得分:18)

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>
        );
    }
}

答案 1 :(得分:11)

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>
        );
      }
    });

答案 2 :(得分:1)

您可以将道具视为儿童,因为技术上children只是另一个道具。这种方法没有错 - React贡献者自己建议你这样做(Support multiple child insertion points)。

此外,如果你在一个庞大的工程师团队中工作,我还建议标准化你如何命名这些道具以及他们的标准行为 - 他们只是简单的内容或回调你可以传递参数。 react-placeholders可以帮助您。因此,组件的代码可能如下所示:

// 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>
    );
  }
}

这就是你嵌入该组件的方式

// 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>
        }}
      />
    );
  }
}

答案 3 :(得分:0)

这个问题启发了我。这是我的方法:

第一个子组件:TestA.tsx

import * as React from 'react';

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

第二个子组件:TestB.tsx

import * as React from 'react';

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

父组件: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>
        )
    }
}

如您所见,您只需要将{this.getComp("YourComponentName")}放置在希望该组件的所有实例出现在父组件中的位置。

用法示例

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

用法非常简单直观。不需要键或其他任何东西,只需插入您的子组件而无需使用怪异的语法即可。

生成的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>

请注意,您可以按任意顺序插入TestAB的子级-它们将插入正确的位置。

所有这些都可以改进,您可以根据需要进行调整。例如,如果您放置除TestA或TestB之外的任何子组件,则不会显示它们,这是我的第一个改进。