将函数作为子组件的组件

本文讲的是将函数作为子组件的组件,


我最近在 Twitter 上发起了关于高阶组件和将函数作为子类的组件的投票,得到的结果让我很意外。

如果你不知什么是“函数作为子组件的组件”,我试图通过这篇文章告诉你:

  1. 函数作为子组件的组件是什么。
  2. 它为什么有用。
  3. 我只想享受分享的快乐,而不是收获一些 Twitter 转发,点赞,或是上一些 newsletter 等等。你懂我的意思吧?

什么是函数作为子组件的组件?

“函数作为子组件的组件”是接收一个函数当作子组件的组件。这种模式的实施和执行得益于 React 的 property types。

class MyComponent extends React.Component{
  render() {
    return (
        <div>
          {this.props.children('Scuba Steve')}
        </div>
    );
  }
}

MyComponent.propTypes = {
  children: React.PropTypes.func.isRequired,
};

没错!通过函数作为子类组件的组件我们就能解耦父类组件和它们的子类组件,让设计者决定选用哪些参数及怎么将参数应用于子类组件。例如:

<MyComponent>
  {(name) => (
    <div>{name}</div>
  )}
</MyComponent>

其他使用这一组件的人可能考虑以不同的方式使用 name ,比如使之作为一个元素的属性:

<MyComponent>
  {(name) => (
    <img src=’/scuba-steves-picture.jpg’ alt={name} />
  )}
</MyComponent>

这里真正奇妙的地方在于,MyComponent ,可以让函数作为子类组件的组件管理状态而不用关心它们是如何使用这些状态的。让我们再来一个更真实的例子。

百分比组件

Ratio 组件将使用设备的宽度,监听 resize 事件并将宽度、高度以及一些描述是否完成尺寸计算的信息传给它的子组件。

首先我们从函数作为子类组件的组件的代码片段开始,这片段在所有子组件函数中都是常见的,它只是让 Comsumer 知道我们期望一个函数作为子组件,而不是 React 节点。

class Ratio extends React.Component{
  render() {
    return (
        {this.props.children()}
    );
  }
}

Ratio.propTypes = {
 children: React.PropTypes.func.isRequired,
};

接下来让我们设计 API ,我们想要一个 X Y 轴的比率,然后我们使用当前的宽度来计算,可以设置一些内部 state 来管理宽度和高度,无论我们是否已经计算了。此外,也该让 propTypes 和 defaultProps 在使用组件时发挥点作用。

class Ratio extends React.Component{  

  constructor() {
    super(...arguments);
    this.state = {
      hasComputed: false,
      width: 0,
      height: 0,
    };
  }

  render() {
    return (
      {this.props.children()}
    );
  }
}

Ratio.propTypes = {
  x: React.PropTypes.number.isRequired,
  y: React.PropTypes.number.isRequired,
  children: React.PropTypes.func.isRequired,
};

Ratio.defaultProps = {
  x: 3,
  y: 4
};

实际上我们还没有做什么有趣的事情,让我们来添加一些事件监听,并计算实际宽度(根据我们比率的变化):

class Ratio extends React.Component{

  constructor() {
    super(...arguments);
    this.handleResize = this.handleResize.bind(this);
    this.state = {
      hasComputed: false,
      width: 0,
      height: 0,
    };
  }

  getComputedDimensions({x, y}) {
    const {width} = this.container.getBoundingClientRect();
return {
      width,
      height: width * (y / x),
    };
  }

  componentWillReceiveProps(next) {
    this.setState(this.getComputedDimensions(next));
  }

  componentDidMount() {
    this.setState({
      ...this.getComputedDimensions(this.props),
      hasComputed: true,
    });
    window.addEventListener('resize', this.handleResize, false);
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.handleResize, false);
  }

  handleResize() {
    this.setState({
      hasComputed: false,
    }, () => {
      this.setState({
        hasComputed: true,
        ...this.getComputedDimensions(this.props),
      });
    });
  }

  render() {
    return (
      <div ref={(ref) => this.container = ref}>
        {this.props.children(this.state.width, this.state.height, this.state.hasComputed)}
      </div>
    );
  }
}

Ratio.propTypes = {
  x: React.PropTypes.number.isRequired,
  y: React.PropTypes.number.isRequired,
  children: React.PropTypes.func.isRequired,
};

Ratio.defaultProps = {
  x: 3,
  y: 4
};

好吧,在这我做了很多东西。我们添加了一些事件监听来监听 resize 事件以及使用提供的比率计算实际的宽度高度。所以我们得到的宽高在组件的 state 里,那我们如何与其他组件共享它们呢?

这是一件难以理解的事情,因为它很容易让人认为“这就完了?”,但事实这就是全部了。

子类组件只是一个 Javascript 函数

这意味着想要计算出宽度和高度,我们只需要提供参数:

render() {
    return (
      <div ref='container'>
        {this.props.children(this.state.width, this.state.height, this.state.hasComputed)}
      </div>
    );
}

现在任何人都可以使用比例组件通过提供的宽度以他们喜欢的方式来正确计算出高度!例如,有人可以使用比例组件来设置 img 上的比例:

<Ratio>
  {(width, height, hasComputed) => (
    hasComputed
      ? <img src='/scuba-steve-image.png' width={width} height={height} />
      : null
  )}
</Ratio>

同时,在另一个文件中,有人决定使用它来设置 CSS 属性。

<Ratio>
  {(width, height, hasComputed) => (
    <div style={{width, height}}>Hello world!</div>
  )}
</Ratio>

在另一个 app 里,有人正根据计算高度使用不同的子类组件:

<Ratio>
  {(width, height, hasComputed) => (
    hasComputed && height > TOO_TALL
      ? <TallThing />
      : <NotSoTallThing />
  )}
</Ratio>

优势

  1. 构造组件的开发人员能自主控制如何传递和使用这些属性。
  2. 函数作为子类组件的组件的作者不强制组件的值如何被利用,允许它非常灵活的使用。
  3. Comsumers 不需要创建另一个组件来决定怎样从“高阶组件”传入属性。高阶组件通常在组成的组件上强制执行属性名称。 为了解决这个问题,许多“高阶组件”提供了一个选择器函数,允许 Comsumers 选择你的属性名称(请参考 redux 连接选择功能)。这不是函数子组件的问题。
  4. 不污染 “props” 命名空间,这允许你同时使用 “Ratio” 组件和 “Pinch to Zoom” 组件,不管它们是否都会计算宽度。高阶组件带有与它们组成的组件相关的隐式契约,不幸的是这可能意味着 prop 的名称会发生冲突以至于高阶组件无法与其他组件进行组合。
  5. 高阶组件在你的开发工具和组件本身中创建一个间接层,例如设置在组件上的常量被高阶组件封装后将无法使用。例如:
MyComponent.SomeContant = 'SCUBA';

然后被高阶组件封装,

exportdefault connect(...., MyComponent);

和你的常量说再见吧。因为如果没有高阶组件提供的函数,你将再也不能访问到这个常量。哭。

总结

大多数时候我们会认为“我需要一个高阶组件来实现这个共享功能!”根据我的经验,我相信在多数情况下函数作为子类组件的组件是一个更好的替代方法来抽象你的 UI 问题,除非你的子组件与其组合的高阶组件真正耦合。

关于高阶组件的不幸事实

补充一下,我认为高阶组件的名称不正确,尽管现在尝试修改已经有点晚了。高阶函数是至少执行一下操作之一的函数:

  1. 将n个函数作为参数。
  2. 返回一个函数作为结果。

事实上,高阶组件做了类似的事情,也就是拿一个组件作为参数并返回一个组件,但是我更容易将高阶组件看作是工厂函数,它是一个能动态创建的组件将允许的功能用于组件的运行组合。然而,在运行组合的时候他们是不知道你的 React 的 state 和 props 。

函数作为子类组件的组件允许你的组件们在作出组合决策时可以访问 state , props 和上下文。当函数作为子组件:

  1. 将一个函数作为参数。
  2. 渲染此函数的结果。

我觉得它们应该被命名为“高阶组件”,因为它像高阶函数只使用组件组合技术而不是功能组合。好吧,现在我们还是继续用“将函数作为子类的组件”这个粗暴的名字。

例子

  1. Pinch to Zoom - Function as Child Component
  2. react-motion 这个项目在讲了很长一段时间这个概念之后,高阶组件才演变出函数作为子类组件的组件。






原文发布时间为:2017年5月10日


本文来自合作伙伴掘金,了解相关信息可以关注掘金网站。

时间: 2024-09-15 14:39:07

将函数作为子组件的组件的相关文章

深入探讨Vue.js组件和组件通信_javascript技巧

基本是按照官网的 Guide 全部梳理了一遍:http://vuejs.org/guide/index.html 这里我们以一个 Todo List 应用为例来把相关的只是都串起来,这篇里面的全部代码都在github上 https://github.com/lihongxun945/vue-todolist  Vue 实例 一个 Vue 应用是由一个 root vue instance 引导启动的,而 Vue instance 是这么创建的: var vm = new Vue({ // opti

Vue.js每天必学之组件与组件间的通信_javascript技巧

什么是组件? 组件(Component)是 Vue.js 最强大的功能之一.组件可以扩展 HTML 元素,封装可重用的代码.在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能.在有些情况下,组件也可以是原生 HTML 元素的形式,以 is 特性扩展. 使用组件 注册 之前说过,我们可以用 Vue.extend() 创建一个组件构造器: var MyComponent = Vue.extend({ // 选项... }) 要把这个构造器用作组件,需要用 `Vue.compone

执行计划组件、组件、老化

原文:执行计划组件.组件.老化 一.执行计划缓冲 优化器生成的执行计划保存在SQL Server内存池中的一个特别部分,被称为计划缓冲或过程缓冲.过程缓冲是SQL Server缓存的一部分.在缓冲中保存计划可使SQL Server避免在重新提交相同的查询时再次通过整个查询优化过程运行.SQL Server支持不同的技术,如:计划缓冲老化,计划缓冲类型来增加缓存的计划的可重用性.它还保存两个被称为hash和查询计划hash的二进制值. 二.执行计划组件 优化器生成的执行计划包含两个组件: 查询计划

PHP开发框架Yii Framework教程(22) UI组件 Zii组件简介

前面介绍了Yii框架支持的部分UI组件,除了前面介绍的UI组件外,Yii框架还提供了Zii组件库,包括列表视图ListView,表 格视图GridView,此外还包括一些基于JQuery的UI组件,如AutoComplete,DataPicker, Button, Drag 和 Drop等,Zii组件主要 定义在包zii.* ,zii.widget.* 开发框架Yii Framework教程(22) UI组件 Zii组件简介-yii2 user组件"> 由于其中部分UI组件和数据源(Dat

PHP中substr_count()函数获取子字符串出现次数的方法_php技巧

本文实例讲述了PHP中substr_count()函数获取子字符串出现次数的方法.分享给大家供大家参考,具体如下: PHP中的substr_count()可用于计算指定字符串中子字符串出现的次数. substr_count()函数定义如下: substr_count(string,substring,start,length) 参数说明: string     必需.规定被检查的字符串. substring  必需.规定要搜索的字符串. start      可选.规定在字符串中何处开始搜索.

ASP 组件|无组件上传

ASP 组件|无组件上传 最后更新:2006-08-25 21:22 纯Javascript打造的ASP上传组件,支持多文件上传.进度条.数据库存放和文件系统存放 组件下载地址(为方便版本同步,未在此处上传附件): http://www.zope.org/Members/Rimifon/FyUpload.sct(右键下载) 相关说明:有组件上传方式请右键"FyUpload.sct"注册,然后可以使用: Server.CreateObject("Rimifon.Upload&q

如何让uml组件图组件中的文本换行且美观。

问题描述 如何让uml组件图组件中的文本换行且美观. 实现不了自己想要的那种.如下两图,要像书上的那样,请说详细一些. 解决方案 文本组件的换行细节

jQuery自定义组件(导入组件)_jquery

1.组件js (function($){ //自定义去除字符串两边空白 String.prototype.trim=function(){ return this.replace(/(^\s*)|(\s*$)/g, ""); } //自定义导入组件 $.fn.customImport = function(methodOroptions,value){ if(typeof methodOroptions == "string"){//存在方法时,调用方法 retur

ASP在线发E-mail的2个函数(使用JMail和NewMail组件)

jmail|mail组件|函数|在线 <%'By Dicky QQ:25941Function JMail(Send_From,Send_To,Send_Subject,Send_Body)'调用JMail组件'发送E-mail函数'參數:'Send_From-發送者信箱'Send_To-接受者信箱'Send_Subject-郵件主題'Send_Body-郵件内容 Dim JMail Set JMail = Server.CreateObject("JMail.Message")