Description
从去年下半年以来开始实践 React Hooks,到现在一年多时间,谈谈感想。
相比 Class 组件
React 官方包括社区里很多大神解释过为什么 Hooks 是更好的选择,从提高编译效率,运行效率,开发体验多个方面都总结出很多很棒的例子。作为普通开发者,我更加看中的是最后一点,因为这与自己的日常工作息息相关,这倒不是说其他两个就不重要了,只是对于个人的影响没有那么深刻。
那么,Hooks 带来了什么好处呢?
首先,使用 Hooks 可以更加方便的描述数据之间的依赖关系,最典型的如 useMemo 的用法,如下,通过 a,b 推导出 c。
const { a, b } = props
const c = useMemo(() => a + b, [a, b])
c 是 jsx 里直接使用的变量,当然,这个例子不太具有说服力。但是这里的数据联动关系更加清晰了。当 a,b 变化时,重新计算 c 的值。
其次,side effect 更加直观,以往在 class 组件中,需要在特定的 life cycle 如 componentDidMount 这样的方法里管理 side-effect,使用 Hooks 可以直接编写。
useEffect(() => {
document.body.addEventListener('resize', onResize)
}, [])
而且,可以很方便的在 effect 退出时就地清理现场。
useEffect(() => {
document.body.addEventListener('resize', onResize)
return () => {
document.body.removeEventListener('resize', onResize)
}
}, [])
而不用像 class 组件中,先将 onResize 保存为实例变量,然后在 compoentWillUnmount 中清理。
最后,也是使用 Hooks 一段时间感受到,除了 useEffect,其他 Hooks 都是没有副作用的纯数据。那将某些可以重用的 Hooks 单独拿出来定义,然后组合起来使用,也是很自然的事情。这就是 custom Hooks。
相比 HOC
在 Hooks 之前,很多重用逻辑的场合,如导入额外的 props,需要使用 HOC。如常见的,withRouter(react-router-dom),withStyles(material-design)。如果需要导入的东西比较多,就会写成这样:withStyles(withRouter(withX(withY(withZ(Component))))),少一个括号就能debug半天。
Hooks 发布之后,这些 lib 也实现了 Hooks 接口,代码看起来会像这样:
function Component() {
const history = useHistory()
const classes = useStyles()
// others
}
可阅读性提高很多。
此外,自己定制 Hooks 也极其简单,比如,我自己做了一个 file picker。
import { useMemo, useEffect } from 'react'
import { useObservable } from 'rxjs-hooks'
import { BehaviorSubject } from 'rxjs'
function useFilepicker() {
const subject$ = useMemo(() => new BehaviorSubject<string>(''), [])
const v$ = useMemo(() => new BehaviorSubject<{ name?: string; content?: string } | null>(null), [])
const v = useObservable(() => v$)
useEffect(() => {
const input = document.createElement('input')
input.type = 'file'
input.name = 'my-file'
input.style.display = 'none'
document.body.appendChild(input)
const onChange: EventListener = (event: Event): any => {
const files = (event.target as HTMLInputElement).files
if (files && files.length) {
const file = files[0]
const fileReader = new FileReader()
fileReader.readAsText(file)
fileReader.onload = (e: ProgressEvent<FileReader>): any => {
console.log('File read operation successfully complete')
const result = e.target ? (e.target.result as string) : ''
v$.next({
name: file.name,
content: result,
})
}
}
return
}
input.addEventListener('change', onChange)
const subscrition = subject$.subscribe((v) => {
console.log(`Received message from handlers: ${v}`)
if (v === 'open') {
input.click()
}
})
return () => {
input.removeEventListener('change', onChange)
document.body.removeChild(input)
subscrition.unsubscribe()
}
}, [subject$, v$])
return useMemo(
() => ({
handlers: {
open: () => subject$.next('open'),
},
value: v,
}),
[subject$, v]
)
}
export default useFilepicker
总结
诞生于 2018 年的 Hooks 已经面世两年时间了,从我个人的体验来说,Hooks 对开发体验的提升是一场革命。开发人员可以用更清晰地数据逻辑来编写组件,更简单地方式来封装数据以及数据相关的逻辑。
我想,我是真的离不开 Hooks 了(笑)。
下面是收藏的一些关于 Hooks 的文章。