【问题标题】:Render React nested components渲染 React 嵌套组件
【发布时间】:2016-12-06 01:42:26
【问题描述】:

有没有办法在不同的 div 下渲染 React 组件的子级?

<Page>
   <Header> ... </Header>
   <Content> ... </Content>
   <Actions> ... </Actions>
</Page>

<div class="page">
   <div class="header-wrapper>
      <div class="header"> ... </div>
   </div>
   <div class="content-wrapper">
      <div class="content"> ... </div>
      <div class="actions"> ... </div>
   </div>
</div>

这里我需要包装器,因为标题的布局与内容和操作所在的区域不同。

显而易见的解决方案可能是引入一个 Body 组件,但是有没有更优雅的方法来抽象出具体的 div 树,它需要从页面声明的逻辑组件(标题、内容、操作)中表示特定布局.

另一个可行的解决方案是按照 React 文档中的建议将 Header、Content 和 Actions 作为属性传递:

<Page
   header={
      <Header> ... </Header>
   }
   content={
      <Content> ... </Content>
   }
   actions={
      <Actions> ... </Actions>
   }
/>

谢谢, 乔治

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    React 允许您访问单个子项

    <Page>
       <Header> ... </Header>
       <Content> ... </Content>
       <Actions> ... </Actions>
    </Page>
    

    内部&lt;Page /&gt;组件渲染

    render() {
      // when multiple elements are passed children prop is an array.
      // extract each of the element passed from the array and place them     
      // wherever needed in the JSX
      const [HeaderView, ContentView, ActionsView] = this.props.children;
    
      <div class="page">
        <div class="header-wrapper>
          {HeaderView}
        </div>
        <div class="content-wrapper">
          {ContentView}
          {ActionsView}
        </div>
      </div>
    }
    

    【讨论】:

    • 这不是只有在所有孩子都在场并且按确切顺序出现的情况下才适用吗?在一般情况下, Page 的实例可能没有页脚或缺少操作 - 我想必须单独检查子项。另外我还有一个单独的question,关于如何控制哪些孩子可以接受
    • @GeorgeS。如果您依赖孩子,则顺序肯定很重要,但是即使缺少跟踪元素,上面的代码也可以工作。如果缺少中间元素,则传递空
      。但我认为&lt;Page /&gt; 不是一个很好的可重用抽象,除非你的网站是一个内容驱动的网站而不是功能。页面通常没有完全相同的部分。所以如果你的部分变化很大,你最好不要有像 这样的组件
    【解决方案2】:

    使用React.Children.toArray 将您的 props.children(这是一个不透明的数据结构)转换为数组并安全地重新组织它们。

    转换为数组后,您可以使用findforEach 方法获取每一个 - Header、Content 和 Action - 通过测试项目的类型。

    const children = React.Children.toArray(this.props.children);
    const headerView = children.find(child => child.type.name === 'HeaderView');
    const contentView = children.find(child => child.type.name === 'ContentView');
    const actionsView = children.find(child => child.type.name === 'ActionsView');
    
    <div class="page">
      <div class="header-wrapper>
        {headerView}
      </div>
      <div class="content-wrapper">
        {contentView}
        {actionsView}
      </div>
    </div>
    

    记得用类或函数声明来声明 HeaderView、ContentView 和 ActionsView,而不是像这样的匿名函数:

    const HeaderView = props => {
      // some stateless rendering
    };
    

    否则他们可能没有type.name 可供测试。

    【讨论】:

      【解决方案3】:

      在 Aurelia 中,您可以使用命名插槽来实现此结果,但在反应中,我发现最好的解决方法是这样做:

      // ...
      
      const header = <Header someProp={someVal} />;
      const content = <Content someOtherProp={someOtherVal} />;
      const actions = <Actions action1={someAction} action2={someOtherAction} />;
      
      return (
        <Page header={header} content={content} actions={actions} />
      );
      

      在 page.jsx 中:

      import React from 'react';
      import PropTypes from 'prop-types';
      
      export const Page = ({ header, content, actions }) => (
        <div className="page">
          <div className="header-wrapper">
            <div className="header">
              {header}
            </div>
          </div>
          <div className="content-wrapper">
            <div className="content">
              {content}
            </div>
            <div className="actions">
              {actions}
            </div>
          </div>
        </div>
      );
      
      Page.propTypes = {
        header: PropTypes.node.isRequired,
        content: PropTypes.node.isRequired,
        actions: PropTypes.node.isRequired,
      };
      

      【讨论】:

        猜你喜欢
        • 2016-06-20
        • 1970-01-01
        • 1970-01-01
        • 2021-10-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多