MST

星途 面试题库

面试题:React Hooks在TypeScript中自定义Hook的类型设计与复用性

要求设计一个自定义Hook,用于处理复杂的数据获取逻辑,例如带有缓存、重试机制的数据请求。该Hook需在TypeScript环境下,具有良好的类型定义和复用性。请阐述设计思路,包括如何定义输入输出类型,如何处理不同的数据请求状态(loading、success、error),并给出完整的代码实现。
46.4万 热度难度
前端开发React

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 输入输出类型定义
    • 输入类型:定义请求的参数类型,比如请求的URL、请求方法、请求体等。
    • 输出类型:包含数据请求的不同状态(loading、success、error)以及对应的数据或错误信息。
  2. 处理不同数据请求状态
    • loading:在发起请求时,将状态设为loading。
    • success:请求成功后,将状态设为success,并存储返回的数据。
    • error:请求失败时,将状态设为error,并存储错误信息。
  3. 缓存处理:使用一个变量来存储已经请求过的数据,下次请求相同参数时,直接从缓存中返回数据。
  4. 重试机制:在请求失败时,根据设定的重试次数进行重试。

代码实现

import { useState, useEffect } from'react';

// 定义请求参数类型
type RequestParams = {
    url: string;
    method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
    body?: any;
};

// 定义请求返回的数据类型
type ResponseData = any;

// 定义请求状态
type RequestStatus = 'loading' |'success' | 'error';

// 定义Hook返回的类型
type UseDataFetchResult = {
    status: RequestStatus;
    data: ResponseData | null;
    error: Error | null;
};

// 自定义Hook
const useDataFetch = (params: RequestParams, retryCount = 3, cache: { [key: string]: ResponseData } = {}): UseDataFetchResult => {
    const [status, setStatus] = useState<RequestStatus>('loading');
    const [data, setData] = useState<ResponseData | null>(null);
    const [error, setError] = useState<Error | null>(null);

    const cacheKey = JSON.stringify(params);

    useEffect(() => {
        if (cache[cacheKey]) {
            setStatus('success');
            setData(cache[cacheKey]);
            return;
        }

        const controller = new AbortController();
        const fetchData = async () => {
            let currentRetry = 0;
            while (currentRetry < retryCount) {
                try {
                    const response = await fetch(params.url, {
                       ...(params.method && { method: params.method }),
                       ...(params.body && { body: JSON.stringify(params.body) }),
                        signal: controller.signal
                    });
                    if (!response.ok) {
                        throw new Error(`HTTP error! status: ${response.status}`);
                    }
                    const result = await response.json();
                    cache[cacheKey] = result;
                    setStatus('success');
                    setData(result);
                    break;
                } catch (e: any) {
                    if (e.name === 'AbortError') {
                        return;
                    }
                    currentRetry++;
                    if (currentRetry === retryCount) {
                        setStatus('error');
                        setError(e);
                    }
                }
            }
        };

        fetchData();

        return () => {
            controller.abort();
        };
    }, [params, retryCount, cache]);

    return { status, data, error };
};

export default useDataFetch;