使用react-hooks实现异步获取数据的封装

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等,具体实现可以自己扩展。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值