前端---自定义hook函数

 

思路: 单独的文件js、ts , 里面借助现有的use函数比如useState,useEffect,useCallBack

实现封装逻辑复用。

标准结构: useTest.ts/js

import {useEffect,useRef,useState,useCallback} from 'react'

export fuction useTest<T = unknown>(参数){  // T是返回类型,参数

  const [result,setResult]=useState<T | null>(null); // 定义返回的结果数据类型T统一

  useEffect (()=>{

  // 其他计算逻辑

  setResult('计算后的结果');

  },[参数变化]);

 return result     ; 

}

调用:

import { useTest } from './useTest'

 const debounced = useTest<string>(  'sd',300)

const debounced = useTest<{ items: any }>({ items: [] }300) 

 

 

 

案例一 

// useDebounce.ts输入防抖

import { useEffect, useMemo, useRef, useState } from 'react'

export function useDebounce<T>(value: T, delay = 300) { // T是返回类型, 括号里是参数

  const [debounced, setDebounced] = useState<T>(value)                     // 保存防抖后的值

  const timer = useRef<number | null>(null)                             // 记录定时器 id

  useEffect(() => {

    if (timer.current) window.clearTimeout(timer.current)               // 新变更来了先清除旧定时器

    timer.current = window.setTimeout(() => setDebounced(value), delay) // delay 后再真正更新

    return () => { if (timer.current) window.clearTimeout(timer.current) }

  }, [value, delay])

  return debounced                                                       // 返回稳定后的值

}

具体调用:

import { useDebounce } from './useDebounce'

 const debounced = useDebounce<string>(  'sd',300)

const debounced = useDebounce<{ items: any }>({ items: [] }, 300) 

 

案例二  自定义useFetch

// useFetch.ts

import { useEffect, useRef, useState, useCallback } from 'react'

 

/**

 * useFetch: 最小但实用的请求 Hook

 * @param url 请求地址(字符串或返回字符串的函数,便于依赖外部状态)

 * @param options fetch 的配置项(可选)

 * @param deps 触发重新请求的依赖数组(可选,默认只在 url/options 变化时请求)

 */

export function useFetch<T = unknown>(

  url: string | (() => string),

  options?: RequestInit,

  deps: any[] = []

) {

  const [data, setData] = useState<T | null>(null)               // 保存返回数据

  const [error, setError] = useState<unknown>(null)              // 保存错误

  const [loading, setLoading] = useState(false)                  // 加载状态

  const abortRef = useRef<AbortController | null>(null)          // 记录当前请求控制器

 

  const getUrl = useCallback(() => (typeof url === 'function' ? url() : url), [url]) // 统一拿 URL

 

  const fetchData = useCallback(async () => {

    // 每次请求前先取消上一次未完成的请求,避免竞态

    abortRef.current?.abort()                                    // 取消上一次

    const controller = new AbortController()                     // 新建控制器

    abortRef.current = controller                                 // 存起来以便取消

    setLoading(true)                                             // 开始加载

    setError(null)                                               // 清空错误

 

    try {

      const res = await fetch(getUrl(), {                        // 发起请求

        ...options,                                              // 透传外部配置

        signal: controller.signal                                // 绑定可取消信号

      })

      if (!res.ok) throw new Error(`HTTP ${res.status}`)         // 非 2xx 视为错误

      const json = (await res.json()) as T                       // 解析 JSON

      setData(json)                                              // 写入数据

    } catch (e: any) {

      if (e?.name !== 'AbortError') setError(e)                  // 被取消不算错误

    } finally {

      setLoading(false)                                          // 结束加载

    }

  }, [getUrl, options, ...deps])                                 // url/options/外部依赖变化时重建

 

  useEffect(() => {

    fetchData()                                                  // 首次或依赖变化时自动请求

    return () => abortRef.current?.abort()                       // 组件卸载时取消请求

  }, [fetchData])                                                // 依赖 fetchData

 

  const refetch = useCallback(() => {                            // 手动重新拉取

    fetchData()

  }, [fetchData])

 

  return { data, error, loading, refetch }                       // 导出状态与动作

}

具体调用:

import { useFetch } from './useFetch'

 const { data, error, loading, refetch } = useFetch<{ items: any[] }>(    // 指定返回类型

    () => `https://api.github.com/search/repositories?q=${encodeURIComponent(q)}`,

    { method: 'GET' },                                                     // 可选 fetch 配置

    [q]                                                                    // q 变化自动重新请求

  )

 

posted @ 2025-08-23 16:33  JavAndroidJSql  阅读(14)  评论(0)    收藏  举报