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 条评论
登录 后参与评论

相关文章

来自专栏林德熙的博客

使用 Resharper 快速做适配器

如果需要做一个类的重写,需要重新写这个类的所有属性和函数,本文提供一个简单的方法让大家快速重写一个类的所有属性和函数。

361
来自专栏学习力

《Java从入门到放弃》框架入门篇:Struts2的常用验证方式

1868
来自专栏韩伟的专栏

反射,框架的利器

通用型软件框架的难题 假设我们希望开发一套通用型的软件框架,这个框架允许用户自定义大量不同的情况下的回调函数(方法),用来实现丰富多彩的业务逻辑功能,例如一个...

35410
来自专栏木宛城主

Unity应用架构设计(6)——设计动态数据集合ObservableList

什么是 『动态数据集合』 ?简而言之,就是当集合添加、删除项目或者重置时,能提供一种通知机制,告诉UI动态更新界面。有经验的程序员脑海里迸出的第一个词就是 O...

1647
来自专栏林德熙的博客

win10 uwp 进度条 Marquez

本文将告诉大家,如何做一个带文字的进度条,这个进度条可以用在游戏,现在我做的挂机游戏就使用了他。

431
来自专栏何俊林

Android Multimedia框架总结(十)Stagefright框架之音视频输出过程

前言:上篇文中最后介绍了数据解码放到Buffer过程,今天分析的是stagefright框架中音视频输出过程: 先看下今天的Agenda: 一张图回顾数据处理...

2068
来自专栏ytkah

两个js冲突怎么解决?试试这四个方法

  两个js冲突很让前端头疼,虽然jquery是通用的,但调用不同经常会出问题。jQuery是目前流行的JS封装包,简化了很多复杂的JS程序,JQuery讲浏览...

3307
来自专栏程序员的知识天地

深入解剖前端,你不知道的Web 组件标准

组件化使得复杂的前端结构变得清晰,各个部分独立起来,高内聚低耦合,使得维护成本大大降低。

373
来自专栏互联网技术杂谈

flume 1.8.0 开发之RPC

flume开发基础可见:https://cloud.tencent.com/developer/article/1195082

2715
来自专栏游戏开发那些事

【python游戏编程之旅】第三篇---pygame事件与设备轮询

本系列博客介绍以python+pygame库进行小游戏的开发。有写的不对之处还望各位海涵。

813

扫码关注云+社区