用 Hooks 的思想开发前端
前言
在 React Hooks 发布以来,大家对 Hooks 的接受程度差别很多。
对于一个习惯了 Hooks 的前端开发来说,真的不明白为什么还有人不去尝试 Hooks?
可能是还没真正感受到 Hooks 的强大之处。因为 Hooks 一开始发布的时候,我就去尝试过这个新 API,但是对于官网的 DEMO 和编写的 DEMO 来说,并未体会到和 Component 多大的差别。
Hooks 和 Component 的差别
很多人可能和我刚开始一样,不明白为什么 Hooks 这么深受欢迎,它和 Component 的差别在哪里。
其实它们之间最大的差别在于,Component 是对于组件的抽取,而 Hooks 是对于 逻辑 的抽取。他其实可以理解为一个响应式的公用的函数。
从弹窗开关讲 Hooks
前端对于模态框肯定不会陌生,一个网站或多或少都有几个模态框,对于模态框组件我们简单实现一个:
import * as React from 'react'
import { createPortal } from 'react-dom'
export interface IModalProps {
visible: boolean
onClose: () => void
children: React.ReactNode
}
const Modal: React.FC<IModalProps> = props => {
// 根据 props.visible 处理显示隐藏等逻辑
// ...
return createPortal((
<div className='modal'>
{props.children}
</div>
), document.body)
}
export default Modal
那么父组件使用这个弹窗的时候,就需要点击打开弹窗,点击“关闭”时关闭弹窗
Component 使用方式
import * as React from 'react'
import Modal from './Modal'
interface IDemoState {
visible: boolean
}
class Demo extends React.Component<any, IDemoState> {
constructor(props: any) {
super(props)
this.state = {
visible: false,
}
}
public render() {
return (
<>
<button onClick={this.open}>
open modal
</button>
<Modal
visible={this.state.visible}
onClose={this.close}>
children
</Modal>
</>
)
}
private open = () => {
this.setState({
visible: true,
})
}
private close = () => {
this.setState({
visible: false,
})
}
}
Function Component 方式
在函数组件中使用 useState
代替 React Component 中的 State
import * as React from 'react'
import Modal from './Modal'
const Demo: React.FC = () => {
const [visible, setVisible] = React.useState(false)
const open = React.useCallback(() => {
setVisible(true)
}, [])
const close = React.useCallback(() => {
setVisible(false)
}, [])
return (
<>
<button onClick={open}>
open modal
</button>
<Modal
visible={visible}
onClose={close}>
children
</Modal>
</>
)
}
用 useState
代替了 Component 中的 State,在代码量上已经减少了很多,但是在实际开发中,可能会有中模态框,那么就有大量的 visible
、open
、close
这种模态框控制变量。
使用 Hooks 的方式
在大量定义 visible
、open
、close
这种重复的变量,重复的逻辑,在以前 Component 的开发方式中,很难将这些复用,就会使用 HOC 等方式解决
好在 Hooks 出现了,我们将这部分抽取成一个 useTrigger
的 Hook 函数
export function useTrigger(initialValue = false) {
const [visible, setVisible] = React.useState(initialValue)
const open = React.useCallback(() => {
setVisible(true)
}, [setVisible])
const close = React.useCallback(() => {
setVisible(false)
}, [setVisible])
return {
visible,
open,
close,
}
}
这样子的话我们修改一下上面对 Modal 的用法
const Demo: React.FC = () => {
const trigger = useTrigger()
return (
<>
<button onClick={trigger.open}>
open modal
</button>
<Modal
visible={trigger.visible}
onClose={trigger.close}>
children
</Modal>
</>
)
}
没错,这就是 Hooks,在用到 Modal 的地方一个 const trigger = useTrigger()
就解决了大量的重复工作。
其他 Hooks
在 React 开发过程中,其实还有很多类似的地方,一直做着重复的工作占用了我们大量的时间,比如在使用输入框时定义了一次又一次的 value
和 handleChange
,同样都可以用 Hooks 的方式解决
export function useInputChange(defaultValue = '') {
const [value, setValue] = useState(defaultValue)
const handleChange = useCallback((evt: React.ChangeEvent) => {
const { value } = evt.target as HTMLInputElement
setValue(value)
}, [])
return {
value,
handleChange,
}
}
还有在页面装载完成后去请求数据等
import { useState, useEffect } from 'react'
export function useFetch(opts) {
const [loading, setLoading] = useState(true)
const [data, setData] = useState(null)
const loadData = async () => {
// ... 加载数据等逻辑
}
useEffect(() => {
loadData()
}, [])
return {
loading,
data,
loadData,
}
}
const Demo = () => {
const { data, loading } = useFetch('https://api.com')
return (
// ... render data
)
}
没有什么是一个 Hook 解决不了的,如果有,就两个!
在 Vue 中同样类似,以前 Vue 发布过一个 vue-functional-api
,其实就是 Vue 版 的 React Hooks,现在已经变成了 composition-api