A Note from the Author (acdlite, Oct 25 2018):
Hi! I created Recompose about three years ago. About a year after that, I joined the React team. Today, we announced a proposal for Hooks. Hooks solves all the problems I attempted to address with Recompose three years ago, and more on top of that. I will be discontinuing active maintenance of this package (excluding perhaps bugfixes or patches for compatibility with future React releases), and recommending that people use Hooks instead. Your existing code with Recompose will still work, just don't expect any new features. Thank you so, so much to @wuct and @istarkov for their heroic work maintaining Recompose over the last few years.
Recompose 及 React Hooks 介绍
Recompose篇
React 中的组件形式
在认识Recompose之前,先重温几个概念。
类组件和函数式组件
概念上的东西不再赘述。简单说一下函数式组件的特点和优点:
特点:
优点:
有状态组件和无状态组件
我们将一个组件是否拥有
this.state
作为判断有无状态组件的唯一标准。广义上无状态的类组件也可以被称为无状态组件,但考虑到你很容易改写一个无状态的类组件
为有状态组件,所以提倡由函数式组件来创建无状态组件。展示组件和容器组件
何为展示组件,顾名思义:只关心组件的展示。它一般是是无状态组件但也有可能是有状态(状态也只能是ui相关的状态)组件,但它一定是
很纯的
一个组件,即没有任何副作用,就像你用咖啡豆加水到咖啡机里去,出来的就是咖啡,不会是82年拉菲。这边的咖啡机就是展示组件。容器组件则关心事物如何运作,通常说明数据是如何加载和变化的。如同现在多了一个咖啡师,他用不同的咖啡豆和其他的材料能通过咖啡机制作出不同口味的咖啡一样。但是容器组件一定不涉及展示的事,也就是说我虽然会做咖啡,但不代表我能造一个咖啡机出来呀。
记住一句话:有状态的组件没有渲染,有渲染的组件没有状态。
展示组件+容器组件
这样的实践一直都是社区提倡的最佳实践,这样的模式有几个好处:什么是 Recompose?
Recompose 是一個 React 工具库。用于
function component
和higher-order-component
。可以把它想象为是给 React 使用的 lodash。Recompose 旨在让使用的人忘记类组件,全部通过
function component + hoc
来构建我们的应用。Recompose 能做什么?
提升状态到
function wrapper
借用
withState
或者withStateHandlers
这类helper提供一个更好的方式来描述state的更新:执行大部分React常见的pattern
像是
lifecyle
、componentFromProp
、withContext
,几乎所有的类组件可以实现的东西都可以通过recompose helper + 函数式组件实现。优化render性能
与其他的library共同使用
像是 Relay、Redux 和 RxJS
建立你自己的 library
可以使用recompose提供的utility来构建自己的library,像是像是
shallowEqual()
和getDisplayName()
更好的实践最佳实践
Recompose借助高阶组件,使我们可以将逻辑从视图中抽离出来,做到了有状态的组件没有渲染,有渲染的组件没有状态,并且可以将代码安置到对应的
logic
文件夹下,即在文件目录层面上也做到了视图与逻辑分离。现在我们要实现一个点击按钮切换模态框显隐的场景,通常情况下,遵循容器组件+展示组件的最佳实践我们会这么做:
这边扯一个不是今天的主角,通过
render props
我们也可以这样实现:现在我们通过Recompose来改写这个例子:
使用Recompose有什么优点呢?
Wrapper
组件还是涉及到了render
部分,但recompose
可以做到真正的ui和状态的隔离toggleShow
的场景,我们只要再一次withToggle
即可,而第一种Wrapper
的形式,好像只能wrapper多次了。更完备的例子
React Hooks 篇
React Hooks和Recompose的关联
这是Recompose作者acdlite在10月25号更新的内容,这里大致翻译一下:
嗨!创建Recompose距离现在已经有三年时间啦。在此的一年后,我加入了React开发团队。今天我们推出了React Hooks,Hooks解决了我三年前试图用Recompose解决的所有问题,此外还有更多问题。我将停止对Recompose的维护(但是解决一些bug和兼容未来的React这些工作还是会进行的),并且推荐你使用Hooks替代Recompose。不过请放心,之前使用Recompose构建的代码依旧可以运行,只是不要期待会有新功能出现了[捂脸哭]
这段话不难看出,作者似乎准备放弃Recompose了,因为Recompose 能做的事
React Hooks
都能做...初识 React Hooks ?
Hooks是React v16.7.0-alpha中加入的新特性。它可以让你在class以外使用state和其他React特性。
使用Hooks改写一下上面那个例子:
为什么会有 React Hooks ?
包含逻辑的状态复用解决方案
高阶组件和上面简单提到的
render props
都是为了解决逻辑复用的问题,但这两种方案都不可避免的会碰到一个问题:我们的组件会被层层叠叠的provider包裹着:这时候,就需要一个更底层的方案来解决逻辑状态复用的问题,所以
Hooks
应运而生。复杂的组件难以理解
类组件所带来的问题
内置Hook介绍
useState
这个hook可以让你在函数式组件中使用state
API:
Demo:
useEffect
useEffect
允许你在函数式组件做一些原本在类组件生命周期里做的事,包括订阅、定时器等其他副作用。API:
如果你返回了一个函数,React会在组件卸载的时候调用它:
由于每次render都会触发
useEffect
的执行,所以你还可以指定第二个参数didOnValueChange
:这时候只有在props.source改变时才会重新执行
useEffect
。useContext
另一种使用context的方式 API:
Demo:
useReducer
同
useState
类似,更适合更复杂的状态改变使用。 API:Demo:
第三个参数
initialAction
会在初始渲染的时候调用。有一点需要注意的是,这里并不会持久化数据,如果要正在实现一个Redux功能,可以同
useContext
一起使用。useReducer
相比useState
更适合处理更复杂的逻辑状态中使用,虽然你可以使用多个useState
。useCallback
优化相关。不会重新生成function API:
Demo:
以往在这种场景,每次render都会生成新的function,可能带来潜在的性能问题,现在通过useCallback可以优化它。
useMemo
优化相关,缓存计算的值 API:
参考reselect的使用场景,当你的某个属性是多个其他几个props计算得来的,为了避免每次render都带来不必要的计算,可以使用
useMemo
:useRef
另一种使用ref的方式 API:
Demo:
useImperativeMethods
API:
需要同
forwardRef
一同使用:useMutationEffect
同
useEffect
一致的接口,但是,在更新兄弟组件之前,它会在React执行其DOM突变的同一阶段同步触发。 使用它来执行自定义DOM突变。API:
使用场景有待更进一步了解,官方推荐尽可能选择
useEffect
。useLayoutEffect
同
useEffect
一致的接口,但它在所有DOM突变后同步执行。使用它来从DOM读取布局并同步重新渲染。useLayoutEffect在浏览器绘制之前,将同步刷新内部计划的更新。组合&自定义Hook
Hook 可以引用其他 Hook,允许我们组合和自定Hook,官网的例子:
这其中,
useFriendStatusBoolean
与useFriendStatusString
是有状态的组件,他们并没有涉及到具体的render
,而FriendListItem
和FriendListStatus
涉及到了具体的render
。同样实现了刚才说的那句:有状态的组件没有渲染,有渲染的组件没有状态。但是相比Recompose,抛开性能和jsx嵌套的问题不开,Hook还是需要将logic侵入到函数式组件,不能像Recompose一样借助高阶组件通过props注入的形式来保证函数式组件的纯粹性。
其他一些Hook的使用指南
Hook
use
开头并借助eslint-plugin-react-hooks
辅助使用参考文章