You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写自定义React Hook实现进度条从60到100的递增

正确实现进度条逻辑的自定义Hook

我现有一个点击后从60%开始递增至100%的进度条,已通过setInterval和状态管理实现功能,代码如下:

import React, { useState, useRef } from "react";
import ProgressBar from "./ProgressBar";

export default function App() {
  const [file, setFile] = useState({ status: 0, error: "" });
  const mockRef = useRef(60);

  const initiate = () => {
    const intervalID = setInterval(() => {
      if (mockRef.current >= 100) {
        clearInterval(intervalID);
        setFile((prevState) => ({
          ...prevState,
          status: 100
        }));
      } else {
        setFile((prevState) => ({
          ...prevState,
          status: mockRef.current
        }));
        mockRef.current = mockRef.current + 10;
      }
    }, 200);
  };

  return (
    <div className="App" style={appStyles}>
      <button type="button" onClick={initiate}>
        Click Me!
      </button>
      <ProgressBar bgColor={"#DF8100"} progress={file.status} />
      {file.status === 100 && <span>Upload complete</span>}
    </div>
  );
}

当前代码运行正常,点击按钮后进度条从60%逐步递增至100%,完成后显示“Upload complete”提示。现在希望将initiate函数内的逻辑迁移至自定义Hook中,该Hook需处理setInterval逻辑并维护状态,保持原有功能不变,可接收初始百分比和状态设置器作为参数。我的尝试代码如下:

import { useRef } from "react";

export const useProgress = (state, timer) => {
  const mockRef = useRef(timer);

  const intervalID = setInterval(() => {
    if (mockRef.current >= 100) {
      clearInterval(intervalID);
      return {
        ...state,
        status: 100
      };
    } else {
      mockRef.current = mockRef.current + 10;
      return {
        ...state,
        status: mockRef.current
      };
    }
  }, 200);
};

请问如何正确编写这个自定义Hook?


正确的自定义Hook实现

你的尝试存在两个核心问题:一是Hook初始化时就直接执行setInterval,不符合“点击按钮才触发”的需求;二是通过返回对象无法更新组件状态,应该直接调用传入的状态设置器。以下是修正后的代码:

import { useRef } from "react";

export const useProgress = (initialProgress, setState) => {
  // 保存定时器ID,避免重复触发
  const intervalRef = useRef(null);
  // 维护当前进度的引用
  const progressRef = useRef(initialProgress);

  const initiateProgress = () => {
    // 若已有定时器运行,先清除
    if (intervalRef.current) {
      clearInterval(intervalRef.current);
    }
    // 重置进度到初始值
    progressRef.current = initialProgress;
    
    intervalRef.current = setInterval(() => {
      if (progressRef.current >= 100) {
        clearInterval(intervalRef.current);
        intervalRef.current = null;
        setState(prev => ({ ...prev, status: 100 }));
      } else {
        setState(prev => ({ ...prev, status: progressRef.current }));
        progressRef.current += 10;
      }
    }, 200);
  };

  // 返回触发进度更新的函数
  return initiateProgress;
};

在App组件中使用该Hook

修改原App组件,引入并使用自定义Hook:

import React, { useState } from "react";
import ProgressBar from "./ProgressBar";
import { useProgress } from "./useProgress";

export default function App() {
  const [file, setFile] = useState({ status: 0, error: "" });
  // 传入初始进度60和状态设置器setFile,获取触发函数
  const initiate = useProgress(60, setFile);

  return (
    <div className="App" style={appStyles}>
      <button type="button" onClick={initiate}>
        Click Me!
      </button>
      <ProgressBar bgColor={"#DF8100"} progress={file.status} />
      {file.status === 100 && <span>Upload complete</span>}
    </div>
  );
}

关键说明

  • 定时器控制:新增intervalRef保存定时器ID,避免重复点击创建多个定时器,进度完成后清空引用。
  • 状态更新:直接调用传入的setState(即原组件的setFile)更新状态,确保组件响应进度变化。
  • 进度维护:progressRef跟踪当前进度,保持和原逻辑一致的递增节奏。
  • 触发时机:将定时器创建逻辑封装在initiateProgress函数中,返回给组件由点击事件触发,符合原需求。

内容的提问来源于stack exchange,提问作者lrr59

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 19:23:19