У меня есть индикатор выполнения, который при щелчке начинает свой прогресс с 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);
};
Когда у вас есть что-то работающее в функциональном компоненте, которое вы хотите превратить в хук, часто это проще, чем можно подумать. Обычно вы можете скопировать имеющийся у вас код прямо в хук; заставить хук принимать все, что ему нужно из-за пределов этого кода, в качестве параметров; и вернуть все, что потребуется вызывающему компоненту от ловушки в качестве возвращаемого значения (если есть несколько значений, оберните их в массив [кортеж] или объект).
В вашем случае просто скопируйте все до 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
можно передать в пользовательский хук:
@ lrr59, добавил к ответу еще одну песочницу. пожалуйста, проверьте!!
Я думаю, вам нужно показать несколько загружаемых файлов с их прогрессом. Для этого вы можете определить другой компонент, используя ваш собственный хук и загрузчик jsx вместе.
То же самое упоминается здесь, stackoverflow.com/questions/76137270/…
Спасибо, Амила, одно небольшое редактирование. Если бы состояние было массивом
const [file, setFile] = useState([{ status: 0, error: "" }]);
, то как бы выглядел сеттер