Напишите собственный хук, который устанавливает текущий прогресс: React hooks

У меня есть индикатор выполнения, который при щелчке начинает свой прогресс с 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>
  );
}

Я использую ссылку, чтобы динамически увеличивать прогресс на 10, а когда становится 100, я очищаю его и вывожу сообщение как Upload Complete. Код работает просто отлично.

Песочница: https://codesandbox.io/s/simple-react-progress-bar-forked-yfz9xb?file=/src/useProgress.jsx:0-436

Теперь я хочу, чтобы содержимое внутри initiate переместилось в пользовательские хуки, которые должны позаботиться о функциональности 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);
};
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
3
0
68
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Когда у вас есть что-то работающее в функциональном компоненте, которое вы хотите превратить в хук, часто это проще, чем можно подумать. Обычно вы можете скопировать имеющийся у вас код прямо в хук; заставить хук принимать все, что ему нужно из-за пределов этого кода, в качестве параметров; и вернуть все, что потребуется вызывающему компоненту от ловушки в качестве возвращаемого значения (если есть несколько значений, оберните их в массив [кортеж] или объект).

В вашем случае просто скопируйте все до 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>
    );
}

Обновленная песочница

Вы можете предоставить функцию обработчика 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:

Если состояние является массивом, то fileIndex можно передать в пользовательский хук:

Спасибо, Амила, одно небольшое редактирование. Если бы состояние было массивом const [file, setFile] = useState([{ status: 0, error: "" }]);, то как бы выглядел сеттер

lrr59 29.04.2023 16:37

@ lrr59, добавил к ответу еще одну песочницу. пожалуйста, проверьте!!

Amila Senadheera 29.04.2023 17:23

Я думаю, вам нужно показать несколько загружаемых файлов с их прогрессом. Для этого вы можете определить другой компонент, используя ваш собственный хук и загрузчик jsx вместе.

Amila Senadheera 29.04.2023 17:25

То же самое упоминается здесь, stackoverflow.com/questions/76137270/…

lrr59 29.04.2023 18:26

Другие вопросы по теме