react-hooks作为react新的api,深受广大开发者的欢迎,不仅简化了代码,嵌套,还更符合react函数式编程的思想。react-hooks并不复杂,但是初期可能很多多学并不理解,不能从class写法转变过来。其实class的多个生命周期是可以通过hooks模拟实现的,且在服务端渲染时,hooks也有相对的优点,比如class在服务端渲染时存在的生命周期多次触发。本文就之前项目中相对复杂的组件中使用hooks的例子,抽取一个简单的demo,介绍一下hooks的使用实例。
在业务开发中,一定会遇到数据请求,异步请求就需要判断请求状态,获取请求的数据,在class组件中,我们要抽离这样的公共方法,可以使用hoc解决,但是当获取数据的组件依赖于其他请求数据时,要么需要修改hoc,要么再包裹一层hoc,那么有没有一种方法实现数据获取的通用性呢。在有了hooks之后,这个问题可以得到很好的解决。
实例:封装自定义hook useData
1, 参数
接收 url, query
2, 返回 data,isLoading, isError,setQuery
说明:本文不对hooks的基本用法进行讲解,具体用法可以参考官网。
源码:
import React, { useState, useEffect } from 'react'
import ApiConfig from '@src/config/ApiConfig'
export default function useData(url, options = {}) {
const [query, setQuery] = useState({});
const [ data, setData ] = useState(null);
const [isLoading, setLoading ] = useState(false);
const [isError, setError ] = useState(false)
useEffect( () => {
async function fetchData(){
try{
let res = await ApiConfig.usual.useData(url,query)
console.log(res)
if(res.status == '200'){
setData(res.data)
}
} catch(err){
console.log(err)
setError(true)
}
setLoading(false)
}
setLoading(true)
fetchData()
}, [url,query])
return [ data, setQuery, isLoading, isError ]
}
说明:
1,对要保存的变量使用useState存储
2,useEffect依赖的url, query改变后进行重新请求,useEffect本身并不能写成async的形式,可以在内部定义一个async函数,再执行,这样就可以使用await
3,数据请求的过程中,对isLoading, isError进行设置,还可以进行其他扩展,比如isTimeout,增加中间件,代理等。
好了,一个简单的数据请求封装已经实现了,那么要怎么使用呢,和hoc的区别又是怎样呢。
实例二:找了一个在项目中封装的rfc组件,实现Select组件配置化渲染,该实例只是动态表单组件的一部分。
起名字是InputSelectHooks,组件会用到useState, useRef, useMemo, useCallback, 当然还有上面的useData。
源码:
import React, { useState, useEffect, useMemo, useCallback, useRef } from 'react'
import useData from '@src/components/useData'
import { Select, Input } from '@icedesign/base'
export default function InputSelectHooks(props) {
const defaultDataSource = useRef(props.defaultOptions) //这里只会在第一次初始化时给current赋值
//console.log('defaultDataSource',defaultDataSource)
const timer = useRef(null)
const [ value, setValue ] = useState(props.defaultValue)
const [ data, setQuery, isLoading ] = useData(props.url) //数据改变后会触发该函数组件的重新加载
const [ dataSource, setDataSource ] = useState([])
const getData = (value) => {
setQuery({name:'ceshi'})
}
/**
* 初始化时设置默认选项
* 拉取服务资源时,返回数据后设置选项
*
*/
useMemo( () => {
/**
* 传入默认选项
*/
if(defaultDataSource.current){
let dataArr = [];
dataArr = dataArr.concat(defaultDataSource.current)
defaultDataSource.current = null;
setDataSource(dataArr)
return ;
}
/**
* 未传入默认选项,从服务端拉取资源
*/
if(data){
console.log('data',data)
setDataSource(data.list);
}
}, [data])
const onSelect = useCallback( (a,b) => {
console.log(a)
setValue(a)
},[])
const onSearch = useCallback( (value) => {
if(timer.current){
clearTimeout(timer.current);
timer.current = null
}
timer.current = setTimeout( () => {
setQuery({
[props.alias.value]:value,
...props.params
})
}, 1000)
}, [])
return <div>
<Select
value = {(props.defaultOptions || !isLoading) ? value : undefined}
dataSource = {dataSource}
onChange = {onSelect}
onSearch = {onSearch}
filterLocal={false}
showSearch = {true}
{...props}
>
</Select>
</div>
说明:
1,从上面代码可以看到useData的用法,和hoc有着很明显的区别,hoc是基于代理或者反向继承,可以称之为纵向扩展,而hooks不会产生多余的嵌套,这和hooks的实现原理有关,每次调用hooks会在当前fiberNode的memoizedState上创建一个firstWorkInProgressHook的链表结构,存储hooks,这些hooks通过next连接。hooks可以说是基于全局变量产生的扩展,属于横向函数封装,如果一个组件内多次使用到数据请求,可以调用多次useData()
2,useMemo是用来计算data的,可以理解为计算属性,当计算不够复杂时,也可以不使用,这里主要是兼容初始化
3,useRef保存变量
4,useCallback保存函数,确保函数只创建一次
实例三:使用组件InputSelectHooks
import React, { Component } from 'react'
import useData from '@src/components/useData'
import InputSelectHooks from '@src/components/InputSelectHooks'
export default function Basic() {
return (
<div>
<InputSelectHooks
url = "http://localhost:3000/list"
defaultValue = {'5'}
defaultOptions = {
[
{
value:'5',
label:'初始化'
}
]
}
alias = {
{
label:'name',
value:'id'
}
}
params = {{
code:'110'
}}
></InputSelectHooks>
</div>
)
}
上面组件可以将props抽离为配置文件或者从后端获取。
本文主要介绍了hooks的基本使用,不太复杂,hooks在简化代码上作用很大。上面的例子还可以加入useReducer,useContext等,具体实现可以自己扩展。