代码之家  ›  专栏  ›  技术社区  ›  lrr59

编写一个自定义钩子来设置当前进度:React钩子

  •  3
  • lrr59  · 技术社区  · 1 年前

    我有一个进度条,点击后它的进度从60开始,一直到100。我可以使用settinterval和设置相应的状态来实现这一点。

    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>
      );
    }
    

    我使用ref来疯狂地将进度增加10,当它达到100时,我清除它并将消息显示为 Upload Complete 。代码运行良好。

    沙盒: https://codesandbox.io/s/simple-react-progress-bar-forked-yfz9xb?file=/src/useProgress.jsx:0-436

    现在我想要里面的内容 initiate 将其移动到一个自定义挂钩,该挂钩应负责setinterval功能并设置对象以使功能保持不变。可能是这个cutsom钩子应该以初始百分比作为数字,并且可能是状态设置器。

    知道我怎么写这个自定义钩子吗。这就是我的尝试

    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);
    };
    
    2 回复  |  直到 1 年前
        1
  •  2
  •   T.J. Crowder    1 年前

    当你想把某个功能组件中的某个东西变成钩子时,这通常比人们想象的要容易。通常,您可以将已运行的代码直接复制到钩子中;使钩子接受来自代码外部的任何所需内容作为参数;并从钩子返回调用组件所需的任何值作为返回值(如果有多个值,请将它们封装在数组[tuple]或对象中)。

    在您的情况下,只需在 return 从您的组件进入挂钩,并使挂钩返回 file initiate :

    export const useProgress = () => {
        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 [file, initiate];
    };
    

    然后 App 使用方式如下:

    export default function App() {
        const [file, initiate] = useProgress();
    
        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>
        );
    }
    

    Updated sandbox

        2
  •  1
  •   Amila Senadheera    1 年前

    您可以提供 updater 处理程序函数和 timer 起始值作为参数,这些参数可以根据您使用自定义钩子的位置而更改。

    这也会起作用:

    import { useRef } from "react";
    
    const useProgress = (updater, timer) => {
      const mockRef = useRef(timer);
    
      const initiate = () => {
        const intervalID = setInterval(() => {
          if (mockRef.current >= 100) {
            clearInterval(intervalID);
            updater((prevState) => ({
              ...prevState,
              status: 100
            }));
          } else {
            mockRef.current = mockRef.current + 10;
            updater((prevState) => ({
              ...prevState,
              status: mockRef.current
            }));
          }
        }, 200);
      };
    
      return initiate;
    };
    
    export default useProgress;
    

    在您的 App.jsx

    ...
    ...
    export default function App() {
      const [file, setFile] = useState({ status: 0, error: "" });
      const initiate = useProgress(setFile, 60);
    
      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>
      );
    }
    ...
    

    Codesandbox演示:

    Edit simple react progress bar (forked)

    如果状态是一个数组,那么 fileIndex 可以传递到自定义挂钩中:

    Edit simple react progress bar (forked)