React.js:将一个组件包装到另一个组件中

 dengdaidanlan_738_260 发布于 2023-02-07 13:37

许多模板语言都有"slots"或"yield"语句,允许进行某种控制反转,将一个模板包装在另一个模板中.

Angular有"transclude"选项.

Rails有收益率声明.如果React.js有yield语句,它将如下所示:

var Wrapper = React.createClass({
  render: function() {
    return (
      
before after
); } }); var Main = React.createClass({ render: function() { return (

content

); } });

期望的输出:

before

content

after

唉,React.js没有.如何定义Wrapper组件以实现相同的输出?

3 个回答
  • 运用 children

    const Wrapper = ({children}) => (
      <div>
        <div>header</div>
        <div>{children}</div>
        <div>footer</div>
      </div>
    );
    
    const App = ({name}) => <div>Hello {name}</div>;
    
    const WrappedApp = ({name}) => (
      <Wrapper>
        <App name={name}/>
      </Wrapper>
    );
    
    render(<WrappedApp name="toto"/>,node);
    

    这也称为transclusionAngular.

    children是React中的一个特殊支柱,它将包含组件标签内的内容(这里<App name={name}/>是内部Wrapper,所以它是children

    请注意,您不一定需要使用children,这对于组件是唯一的,并且您可以根据需要使用普通道具,或者混合使用道具和子项:

    const AppLayout = ({header,footer,children}) => (
      <div className="app">
        <div className="header">{header}</div>
        <div className="body">{children}</div>
        <div className="footer">{footer}</div>
      </div>
    );
    
    const appElement = (
      <AppLayout 
        header={<div>header</div>}
        footer={<div>footer</div>}
      >
        <div>body</div>
      </AppLayout>
    );
    
    render(appElement,node);
    

    对于许多用户来说,这很简单,而且我推荐这适用于大多数消费者应用程序.


    渲染道具

    可以将渲染函数传递给组件,通常调用此模式render prop,并且children通常使用prop来提供该回调.

    这种模式并不适用于布局.包装器组件通常用于保存和管理某些状态并将其注入其渲染功能.

    反例:

    const Counter = () => (
      <State initial={0}>
        {(val, set) => (
          <div onClick={() => set(val + 1)}>  
            clicked {val} times
          </div>
        )}
      </State>
    ); 
    

    你可以获得更多的幻想甚至提供一个对象

    <Promise promise={somePromise}>
      {{
        loading: () => <div>...</div>,
        success: (data) => <div>{data.something}</div>,
        error: (e) => <div>{e.message}</div>,
      }}
    </Promise>
    

    请注意,您不一定需要使用children,这是品味/ API的问题.

    <Promise 
      promise={somePromise}
      renderLoading={() => <div>...</div>}
      renderSuccess={(data) => <div>{data.something}</div>}
      renderError={(e) => <div>{e.message}</div>}
    />
    

    截至今天,许多图书馆都在使用渲染道具(React context,React-motion,Apollo ......),因为人们倾向于比HOC更容易找到这个API.react-powerplug是一组简单的渲染道具组件.react-adopt帮助你做作文.


    高阶分量(HOC).

    const wrapHOC = (WrappedComponent) => {
      class Wrapper extends React.PureComponent {
        render() {
          return (
            <div>
              <div>header</div>
              <div><WrappedComponent {...this.props}/></div>
              <div>footer</div>
            </div>
          );
        }  
      }
      return Wrapper;
    }
    
    const App = ({name}) => <div>Hello {name}</div>;
    
    const WrappedApp = wrapHOC(App);
    
    render(<WrappedApp name="toto"/>,node);
    

    一个高阶组件/ HOC通常是一个函数,它的成分,并返回一个新的组件.

    使用高阶组件可能比使用children或更高性能render props,因为包装器可以将渲染短路一步shouldComponentUpdate.

    我们在这里使用PureComponent.当重新渲染应用程序时,如果WrappedApp名称prop不随时间改变,则包装器能够说"我不需要渲染,因为props(实际上,名称)与之前相同".使用children上面的基于解决方案,即使是包装器PureComponent,也不是这种情况,因为每次父呈现时都会重新创建子元素,这意味着即使包装的组件是纯的,包装器也可能总是重新呈现.有一个babel插件可以帮助减轻这种情况,并确保children随着时间的推移不断元素.


    结论

    高阶组件可以为您提供更好的性能.它并不复杂,但一开始看起来肯定不友好.

    阅读本文后,请勿将整个代码库迁移到HOC.请记住,在应用程序的关键路径上,出于性能原因,您可能希望使用HOC而不是运行时包装器,特别是如果使用相同的包装器很多次,则值得考虑将其设置为HOC.

    Redux首先使用运行时包装器,<Connect>然后connect(options)(Comp)出于性能原因切换到HOC (默认情况下,包装器是纯粹的并且使用shouldComponentUpdate).这是我想在这个答案中强调的完美例证.

    请注意,如果组件具有render-prop API,通常很容易在其上创建HOC,因此如果您是lib作者,则应首先编写render prop API,并最终提供HOC版本.这就是Apollo对<Query>渲染道具组件以及graphql使用它的HOC 所做的事情.

    就个人而言,我同时使用两者,但如果有疑问,我更喜欢HOC,因为:

    compose(hoc1,hoc2)(Comp)与渲染道具相比,组合它们()更具惯用性

    它可以给我更好的表现

    我熟悉这种编程风格

    我毫不犹豫地使用/创建我最喜欢的工具的HOC版本:

    React的Context.Consumer补偿

    未说明的 Subscribe

    使用graphql阿波罗的HOC而不是Query渲染道具

    在我看来,有时渲染道具使代码更具可读性,有时更少...我尝试根据我的约束使用最实用的解决方案.有时可读性比表演更重要,有时则不然.明智地选择并且不要紧跟2018年将所有东西转换为渲染道具的趋势.

    2023-02-07 13:38 回答
  • 尝试:

    var Wrapper = React.createClass({
      render: function() {
        return (
          <div className="wrapper">
            before
              {this.props.children}
            after
          </div>
        );
      }
    });
    

    有关详细信息,请参阅文档中的多个组件:子项和子项类型道具.

    2023-02-07 13:38 回答
  • 除了Sophie的回答之外,我还发现了在发送子组件类型时的用法,做了这样的事情:

    var ListView = React.createClass({
        render: function() {
            var items = this.props.data.map(function(item) {
                return this.props.delegate({data:item});
            }.bind(this));
            return <ul>{items}</ul>;
        }
    });
    
    var ItemDelegate = React.createClass({
        render: function() {
            return <li>{this.props.data}</li>
        }
    });
    
    var Wrapper = React.createClass({    
        render: function() {
            return <ListView delegate={ItemDelegate} data={someListOfData} />
        }
    });
    

    2023-02-07 13:39 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有