跳转至

前端面试重要代码内容之 hooks 实现

总结于2025.10

这些是本人在面试的时候总结的React hooks实现相关知识,加*号的是个人认为比较重要的。因为一开始是总结给自己看的,可能部分写法比较草率。如果你认为其中有错误或需要补充,请联系我~

useFetch*

import { useState, useEffect } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  // 用于返回给外部的 Promise,支持 then/catch
  const fetchPromise = new Promise((resolve, reject) => {
    useEffect(() => {
      // 只有 url 存在时才发起请求
      if (!url) {
        setLoading(false);
        setError(new Error('URL is required'));
        reject(new Error('URL is required'));
        return;
      }

      const fetchData = async () => {
        setLoading(true);
        setError(null);
        try {
          const response = await fetch(url);
          if (!response.ok) {
            throw new Error(`HTTP error: ${response.status}`);
          }
          const result = await response.json();
          setData(result);
          resolve(result); // 成功时 resolve
        } catch (err) {
          setError(err);
          reject(err); // 出错时 reject
        } finally {
          setLoading(false);
        }
      };

      fetchData();
    }, [url]); // 依赖 url,url 变化会重新请求
  });

  // 返回数据、加载状态、错误,以及一个 Promise(支持 then/catch)
  return {
    data,
    loading,
    error,
    promise: fetchPromise, // 可以用 .then / .catch 处理这个 Promise
  };
}

usePrevious

import { useRef, useEffect } from 'react';

function usePrevious(value) {
  const ref = useRef(); 

  useEffect(() => {  // useEffect是渲染完成后才会执行的
    ref.current = value; // 在每次渲染后,将当前的 value 存入 ref
  }, [value]); // 仅在 value 变化时触发

  return ref.current; // 返回上一次的 value(即 ref.current)
}

useRequest

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

// 请求缓存:以请求参数的 JSON string 作为 key
const requestCache = new Map();

function useRequest(requestFn, params) {
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);

  // 将参数序列化为字符串作为缓存的 key
  const cacheKey = JSON.stringify(params);

  // 发起请求的核心逻辑
  const doRequest = useCallback(async () => {
    setLoading(true);
    setError(null);

    try {
      // 检查缓存中是否存在当前参数的请求结果
      if (requestCache.has(cacheKey)) {
        const cached = requestCache.get(cacheKey);
        setData(cached);
        setLoading(false);
        return cached; // 直接返回缓存,不重复请求
      }

      // 没有缓存,发起实际请求
      const result = await requestFn(...(Array.isArray(params) ? params : [params]));

      // 更新缓存
      requestCache.set(cacheKey, result);

      // 更新状态
      setData(result);
      return result;
    } catch (err) {
      setError(err);
      throw err; // 可以被外部 .catch 捕获
    } finally {
      setLoading(false);
    }
  }, [requestFn, params, cacheKey]);

  // 手动刷新:清空缓存并重新请求(或者可以选择保留缓存但依然请求最新)
  const refresh = useCallback(() => {
    // 可选:是否清除该 key 的缓存,这里我们选择刷新时仍然请求,但更新缓存
    return doRequest();
  }, [doRequest]);

  // 监听 params 变化时自动执行请求
  useEffect(() => {
    if (params != null) {
      doRequest();
    }
  }, [params, doRequest]);

  return [loading, data, error, refresh];
}

  • site: Muzibing's website owner: Muzibing url: https://muzibing.github.io/ desc: "🎓 B.Eng. in CS from ZJU | 🛠️ Front-end Developer at Meituan now" image: https://github.com/Muzibing/Muzibing.github.io/blob/master/img/qhqq.jpg color: "#5486e9"