React函数式进阶

React让很多人让追捧的一个特性是它的所有的组件都是完全由JavaScript组成的。组件的定义是JavaScript,组件的模板也可以是JavaScript,组件的样式也可以是JavaScript(参考styled-component)。React并没有创造太多概念,唯一的创造品——JSX,其内部的statement也是一段段纯JavaScript代码,并且在Babel编译后依然转变成了JavaScript。

而JavaScript又是一个把函数当作一等公民的语言。函数不仅可以被声明和调用,也可以像值一样做赋值、传参、返回的操作。

这样运行在一个有着first-class functions特性的语言之上的纯JavaScript组件库,自然可以脑洞大开的有很多玩法。

Stateless Component

使用React的同学自然对这个概念一点都陌生。虽然大多数情况下我们都会使用 class extends React.Component 来声明一个Stateful Component,虽然Stateless Component没有完整的生命周期,虽然Stateless Component的性能相比Stateful Component并没有提升,但是它在很多场合下仍然是有意义的。下面是一个最简单的Stateless Component的声明:

function Welcome({name}) {
  return <div>{name}</div>
}

由于这是一个纯函数,我们可以基于它创建一个简单的High Order Component。

const withNameX = WrappedComponent => props => WrappedComponent(Object.assign({}, props, {name: ‘x’}))
const WelcomeX = withNameX(Welcome)

我们也可以对 Welcome 做简单的 compose。

const toUpperCase = props => Object.keys(props).reduce((target, next) => {
  target[next] = props[next].toUpperCase()
  return target
})
const UpperWelcome = compose(Welcome, toUpperCase )

使用Stateless Component好处很多,包括

  • Pure。单元测试很方便。
  • 强制你从更简单的角度思考组件的组织。单个函数的代码量更小,功能更单一。「The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that.」——《Clean Code》
  • 可以使用compose、curry等特性构件可复用组件。

Stateless Component最大的不足是它没有能力在最佳实践的前提下处理需要回传属性的事件回调,我们只能写成这样:

const Trigger = ({onClick, id}) => <Button onClick={() => onClick(id)}>test</Button>

由于每次调用都会生成一个新的匿名函数,子组件是无法利用PureComponent做优化的。这是我在实际工作场景下使用Stateless Component最大的障碍。

recompose

上面的障碍当然也是有解的,recompose是一个为Stateless React爱好者提供的一个工具库。我们可以使用它提供的 withHandlers 方法。

const Trigger = withHandlers({
  onClick: props => () => props.onClick(props.id)
})(({onClick}) => <Button onClick={onClick}>test</Button>)

是不是也很优雅。

当然为了能处理这种类型的回调,withHandlers 内部也是使用了Stateful Component的,感兴趣的同学可以看看recompose的源码。

recompose还有 withState, pure, onlyUpdateForKeys, withContext 等很多实用的工具函数,帮助我们至少从代码编写角度实现全面使用Stateless Component替代Stateful Component。

Function as child Components

这也是React社区一种常见的组件构建方式。它也能解决HOC中丢失上下文、丢失ref的问题。它也能有效的提升代码复用率,而且某些情况下比HOC要更加优雅。

一个最简单的Function as Child Component如下:

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

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

PayPal开源的downshift就是使用Function as Child Component模型来构建他们的autocomplete,dropdown, select等组件的。

一般我们写一个autocomplete组件,是基于Popover -> Menu + InputTrigger -> AutoComplete这样逐步组合、增强基础组件的方式。这么写会有几个问题:高级组件或者完全无法获取底层组件的引用,或者需要通过很奇怪的方式把引用回调一层层传下去;为了适配很多情况和需求,为了能控制各组合组件的行为,高级组件的参数会多的可怕:ant.design的AutoComplete组件有14个参数,material-ui则有27个参数。

Downshift则完全不处理组件的展示和组合,这部分逻辑交给开发者自己,通过Function as Child Components的方式自由设计他们希望的样式和行为。Downshift只处理这一类组件的交互逻辑,维护组件状态,并暴露少数几个必须设置的子组件属性的接口。这样的代码组织,输入输出都很明确,组件间的耦合也很小,不仅解决了参数爆炸的问题,也提升了可维护性。

对比High Order Component与Function as Child Components

HOC

FaCC

使用者无关,HOC帮你完成了一切组件行为

使用者完全大部分组件展示和行为,更可控

HOC在运行时无法获取组件相关的state和props

可以在运行时获取组件的 state & props

HOC可以通过shouldComponentUpdate做优化

FaCC由于每次render都会改变,无法使用shouldComponentUpdate做优化

总结

本文提到了两种组件设计的思路——利用recompose拆分组件为Stateless Component,使用Function as Child Components来剥离行为和展现——以此来提升代码的可读性和可维护性。实际项目中可以按需使用。

参考资料

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序员叨叨叨

当你刷新RecyclerView程序崩掉的时候

今天测试 APP的时候发现一个有关RecyclerView的BUG,我们先上图来看看崩溃情况:

1002
来自专栏小灰灰

Java & PhantomJs 实现html输出图片

Java & PhantomJs 实现html输出图片 借助phantomJs来实现将html网页输出为图片 I. 背景 如何在小程序里面生成一张图,分享到朋...

5298
来自专栏GIS讲堂

web中的树形结构【小结】

最近在做一个项目,是一个b/s架构的,在项目中,用到了树形结构,即如图1所示的结构。

1682
来自专栏张戈的专栏

移动搜索SEO:网站移动适配之Meta标注、移动跳转终结篇

这些天,在给博客的标签页(tag)添加跳转和 META 动态申明时,居然让我醍醐灌顶,发现之前的动态适配的做法是多么的苦逼和小白! 总结前,先来回顾下小白张戈在...

4586
来自专栏大魏分享(微信公众号:david-share)

从PowerVM,KVM到Docker:存储池的配置与调优---第一篇终结(第3子篇)

VIOC 上的 VSCSI 性能调优 在本实验的 VIOC 中,一个磁盘对应 4 条 VSCSI 路径。查看磁盘默认的属性 ; # lsattr -El hdi...

4786
来自专栏Core Net

懒人小技巧, Toad 常用偷懒方法

3149
来自专栏张善友的专栏

CLR 4.0 安全模型

在公共语言运行时(CLR)过往的版本中,安全模型一直是最为复杂的模块之一,由于涉及Evidence,CAS策略等机制,难以被用户使用。在Silverlight中...

1888
来自专栏互联网开发者交流社区

Robots协议

1367
来自专栏跟着阿笨一起玩NET

在大型软件中用Word做报表: 书签的应用

本文转载:http://www.cnblogs.com/huyong/archive/2011/08/24/2151599.html

721
来自专栏子勰随笔

iMac使用过程中的简单故障解决

1241

扫码关注云+社区