资讯专栏INFORMATION COLUMN

React Hooks 之 useFetch

Ryan_Li / 1068人阅读

摘要:而每次捕获出的错误可能需要打印出来以检测。同时有些同学不习惯使用来捕获错误,这就可能造成不可预计的问题。是否立即请求并接受初始化返回值业务我们并不希望初始化的是否立即发送请求。

前言

自 React Hooks 16.8.0 后带来了 React hooks 这一特性。这一特性在没有破坏性的更新下为我们带来了更加舒爽的开发方式。过去我们常常因providers,consumers,高阶组件,render props 等形成“嵌套地狱”。尽管 Class Component 在某种程度上为我们提供了更方便的写法以及生命周期,但同时也带来了一些不好的地方。例如难以理解的 class 内部原理、难以测试的声明周期。而 React Hooks 为我们提供了一种 Function Component 的写法,让我们用更少的代码写出更加优雅、易懂的代码。本文不做 React Hooks API的讲述,如有不懂,请移步 Hooks 简介

发送服务端请求所面临的问题

1. try / catch问题

在开发代码时,我们发送后端请求后接受到的数据,需要使用try/catch来捕获错误。而每次捕获出的错误可能需要打印出来以检测bug。这样我们每次都会写同样的代码,这样在开发过程中很不友好。同时有些同学不习惯使用 try/catch 来捕获错误,这就可能造成不可预计的问题。

import React, { useCallback, useReducer, useEffect } from "react"
import { TimeNumberType, PageType } from "common/constant/interface"

type ParamsType = PageType & TimeNumberType

const reducer = (state: ParamsType, action: Actions) => {
  const { payload } = action
  return { ...state, ...payload }
}
const postListData = (params: ParamsType) => post("/network/api/test/getlist", params)
const initialParams = {
  pageSize: 10,
  pageNumber: 1,
  startTime: 0,
  endTime: 0
}

const ListComponent = () => {
  const [params, dispatch] = useReducer(reducer, initialState)
  const getList = async () => {
    // try catch
    try {
      const res = await postListData(params)
      console.log(res)
    } catch (err) {
      console.error(err)
    }
  }

  useEffect(() => {
    getList()
  }, [params])
}

demo中展示了在业务场景中发送请求的场景,当发送请求多了之后我们会每次手动try / catch,虽然不是大问题,但是重复代码写多了会觉得难受...。下面看第二个功能。

2. 请求状态

在实际的业务场景中,我们向后端发送请求时,往往伴随着用户点击多次,但是只能发送一次请求的问题,这时我们需要手动加锁。并且在很多场景中我们需要知道请求状态来为页面设置loading。例如:

import React, { useCallback, useReducer, useEffect } from "react"
import { TimeNumberType, PageType } from "common/constant/interface"
import { DateRangePicker, Table } from "UI"

type ParamsType = PageType & TimeNumberType

const TIME = Symbol("time")
const PAGE = Symbol("page")
const reducer = (state: ParamsType, action: Actions) => {
  const { payload } = action
  return { ...state, ...payload }
}
const postListData = (params: ParamsType) => post("/network/api/test/getlist", params)
const initialParams = {
  pageSize: 10,
  pageNumber: 1,
  startTime: 0,
  endTime: 0
}

const ListComponent = () => {
  const [params, dispatch] = useReducer(reducer, initialState)
  const [loading, setLoading] = useState(false)
  const [list, setList] = useState({})
  
  const getList = async () => {
    // loading is true
    if (loading) return
    // set loading status
    setLoading(true)
    // try catch
    try {
      const res = await postListData(params)
      setList(res)
      setLoading(false)
    } catch (err) {
      console.error(err)
      setLoading(false)
    }
  }

  useEffect(() => {
    getList()
  }, [params])
  
  return (
      
{ dispatch({ payload: { pageNumber }, type: PAGE }) }} list={list} // 数据是否正在加载,以此来判断是否需要展示loading loading={loading} /> ) }

demo中展示了日期组件以及包含有分页器的 Table组件,当日期发生变更,或者分页器发生变更时,我们需要dispatch来更新请求参数,从而发送请求。在发送请求时如果正在请求,则忽略,而不在请求时需要手动加锁,来防止多次请求。
同时Table需要根据请求状态来判断是否需要展示loading。

解决问题

基于以上的问题,我们能否通过 Hooks 来封装一个 custom hooks来解决问题。

1. 明确目标

custom hooks 解决的问题

解决每个函数都要统一写try/catch的流程

解决发送请求需要手动加锁防止多次重复请求的痛点

不需要在手动useState loading,直接获取loading值

所以我们需要在 custom hooks 中发送请求、暴露出请求后的值、暴露 loading 状态、以及用户可能需要多次请求,这就需要暴露一个勾子。在发生请求错误时可能需要做某些操作,所以还需要暴露在错误时回调的勾子函数。

是否立即请求并接受初始化返回值

业务我们并不希望初始化的是否立即发送请求。 并且能够有初始化的返回值

支持泛型

在TS中,开发者希望能够自定义请求的参数类型,以及请求结果的类型

2. 定义函数

useFetch 函数

import { useState, useEffect } from "react";

/**
 * 1. 解决每个函数都要统一写try/catch的流程
 * 2. 解决发送请求需要手动加锁防止多次重复请求的痛点
 * 3. 不需要在手动useState loading了~,直接获取fetching值
 * 4. (甚至在参数发生变化时只需要传入更改的参数就OK)已删除
 * @param getFunction 发送请求的函数
 * @param params 参数
 * @param initRes 初始化值
 * @param execute 是否立即执行请求函数
 */

// R, P支持泛型
function UseFetch(
  getFunction: any,
  params: P,
  initRes");

3. 如何使用

根据最初的demo我们改造一下代码

import React, { useCallback, useReducer, useEffect } from "react"
import { TimeNumberType, PageType } from "common/constant/interface"
import { DateRangePicker, Table } from "UI"
// 导入 useFetch
import { useFetch } from "custom-hooks" 

type ParamsType = PageType & TimeNumberType
type ListInfo = {list: Array, total: number}

const TIME = Symbol("time")
const PAGE = Symbol("page")
const reducer = (state: ParamsType, action: Actions) => {
  const { payload } = action
  return { ...state, ...payload }
}
const postListData = (params: ParamsType) => post("/network/api/test/getlist", params)
const initialParams = {
  pageSize: 10,
  pageNumber: 1,
  startTime: 0,
  endTime: 0
}

const ListComponent = () => {
  const [params, dispatch] = useReducer(reducer, initialState)
  
  const [list, loading, getList] = useFetch(
    getWithDraw,
    state,
    { list: [], total: 0 },
    false
  )

  useEffect(() => {
    getList()
  }, [params])
  
  return (
      
{ dispatch({ payload: { pageNumber }, type: PAGE }) }} list={list} // 数据是否正在加载,以此来判断是否需要展示loading loading={loading} /> ) }

对比代码我们可以看到中间的请求的代码被我们干掉了,使用 useFetch 来将状态以及发送请求封装在一起。能够让我们写更少的代码。


同时 useFetch的第3个参数当传入的为 null 时,可以模拟请求发送错误,这样我们可以在开发时做兜底方案。

4. 也许并不想要那么多值。

也许有些请求不需要关注请求状态

  // 解构赋值、空着就好
  const [list, , getList] = useFetch(
    getWithDraw,
    state,
    { list: [], total: 0 },
    false
  )

本文完~

如有问题,欢迎指出~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/6780.html

相关文章

  • React Hooks 从入门到上手

    摘要:前言楼主最近在整理的一些资料,为项目重构作准备,下午整理成了这篇文章。给传入的是一个初始值,比如,这个按钮的最初要显示的是。取代了提供了一个统一的。 showImg(https://segmentfault.com/img/bVbpUle?w=900&h=550); Hooks are a new addition in React 16.8. They let you use sta...

    XFLY 评论0 收藏0
  • React Hooks 从入门到上手

    摘要:前言楼主最近在整理的一些资料,为项目重构作准备,下午整理成了这篇文章。给传入的是一个初始值,比如,这个按钮的最初要显示的是。取代了提供了一个统一的。 showImg(https://segmentfault.com/img/bVbpUle?w=900&h=550); Hooks are a new addition in React 16.8. They let you use sta...

    zhouzhou 评论0 收藏0
  • React 异步数据管理思考

    摘要:异步数据管理一直是前端的一个重点和难点,可以这么说,的应用会有异步数请求据并在中消费,并且在相当多的应用中,处理异步数据是它的核心业务逻辑。总结个人认为,异步数据不应该使用状态管理来维护,应该放在组件内。 异步数据管理一直是前端的一个重点和难点,可以这么说,80%的 web 应用会有异步数请求据并在 UI 中消费,并且在相当多的 web 应用中,处理异步数据是它的核心业务逻辑。 在 R...

    BearyChat 评论0 收藏0
  • React Hooks 入门(2019)

    摘要:到目前为止,表达这种流程的基本形式是课程。按钮依次响应并更改获取更新的文本。事实证明不能从返回一个。可以在组件中使用本地状态,而无需使用类。替换了提供统一,和。另一方面,跟踪中的状态变化确实很难。 备注:为了保证的可读性,本文采用意译而非直译。 在这个 React钩子 教程中,你将学习如何使用 React钩子,它们是什么,以及我们为什么这样做! showImg(https://segm...

    GitCafe 评论0 收藏0
  • 精读《Function Component 入门》

    摘要:比如就是一种,它可以用来管理状态返回的结果是数组,数组的第一项是值,第二项是赋值函数,函数的第一个参数就是默认值,也支持回调函数。而之所以输出还是正确的,原因是的回调函数中,值永远指向最新的值,因此没有逻辑漏洞。 1. 引言 如果你在使用 React 16,可以尝试 Function Component 风格,享受更大的灵活性。但在尝试之前,最好先阅读本文,对 Function Com...

    Scholer 评论0 收藏0

发表评论

0条评论

Ryan_Li

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<