Node, React, Hooks, только на стороне клиента
Я хочу иметь непрерывный l oop для API с очередью задач и задержками между принудительными вызовами (для этого l oop) нет Независимо от того, из какого компонента поступают запросы на вызовы.
Я также решил сделать визуальный процесс, чтобы упростить просмотр вызовов, поэтому он создан как компонент.
Вот упрощенный пример проблемы: asyn c l oop работает за пределами новых экземпляров компонента, и я попытался получить состояние тремя способами с помощью вывода l oop меньшим шрифтом и кнопкой, чтобы изменить состояние:
заключено state
(предоставляется из контекста)
передано в качестве параметра ref st
и даже refState
из useRef ( состояние)
https://codesandbox.io/s/compassionate-einstein-qyq87?fontsize=14&hidenavigation=1
Вы можете нажать кнопку добавления задачи и заметить, что внутренняя до l oop длина задачи не обновляется, поскольку это запущено. Я также могу отправить изнутри, но без текущего состояния, я сбрасываю глобальное состояние. Я добавил точечную анимацию, чтобы вы могли видеть, что l oop жив.
Я нашел решение, которое кажется хакерским, поэтому я не хочу, чтобы оно было «исправлено», поэтому искал более разумное решение:
РЕШЕНИЕ МОЖЕТ: добавить клонированный useState
, который копирует состояние в клон при каждом изменении состояния (с useEffect
), использовать setClone внутри в форме обратного вызова, чтобы получить состояние из «предыдущего значения». Непосредственное использование клона НЕ дает последнее состояние.
const { state, dispatch } = React.useContext(Store);
const [clone, setClone] = React.useState(state);
React.useEffect(() => setClone(state), [state]);
// api loop
const apiLoop = async () => {
let haxState;
setClone(val => {
haxState = val; // copying value
return val; // not changing anything
});
----------
await new Promise(r => setTimeout(r, 1000));
// loop self
apiLoop();
};
const [isLooping, setLooping] = React.useState(false);
if (!isLooping) {
apiLoop(); // start
setLooping(true); // label started so don't restart
}
https://codesandbox.io/s/late-leftpad-5x2ti?fontsize=14&hidenavigation=1&theme=dark
Я был очень счастлив найти что-то совместимое с реагированием, которое позволит мне прочитать задачи из (своего рода) глобального состояния, но я могу sh Я мог бы сделать это непосредственно с объектом состояния вместо того, чтобы задействовать дополнительные useEffect
и совершенно новые useState
просто для клонирования моего состояния. Я также не уверен, может ли мой редуктор или контекст сделать что-то похожее на форму setState (value => b), чтобы получить последнее значение, оно было разработано, чтобы изменять значение, а не захватывать его, поэтому я нервничаю.
Я не очень заинтересован в каких-либо решениях, где останавливается l oop. Клиент может выдвигать на него сотни задач, которые не могут перекрываться, повторяться или постоянно останавливаться. Предположим, что не может быть какой-либо обработки на стороне сервера, чисто на стороне клиента. (не показано событие при разгрузке, которое убивает l oop при необходимости)
Любые идеи или мое решение не случайно и нормально использовать?
Спасибо
Обновление:
Может быть, я обдумывал это. Я забыл, что могу один раз использовать объекты classi c js, связанные ссылками, размещать их вне компонента, чтобы они не инициализировались при повторном рендеринге, и использовать их для получения самой последней версии состояния
import React from "react";
import { Store } from "./state";
import "./styles.css";
// value placed outside component where it's not remade
const last = {};
export const App = () => {
// global state
const { state, dispatch } = React.useContext(Store);
// update reference for last.state object without updating reference for last object
// so last still points to same const above but last.state points to newest state
last.state = state;
let a = 0; // little counter
// api loop
const apiLoop = async () => {
if (window.output)
window.output.innerHTML = `
apiLoop:\n
last.state.tasks: ${last.state.tasks.length}\n
${".".repeat((a++ % 3) + 1)}
`;
// 1000ms api delay for rate limit
await new Promise(r => setTimeout(r, 1000));
// loop self
apiLoop();
};
// initialize loop
const [isLooping, setLooping] = React.useState(false);
if (!isLooping) {
apiLoop(); // start
setLooping(true); // label started so don't restart
}
https://codesandbox.io/s/heuristic-dijkstra-27p36?fontsize=14&hidenavigation=1&theme=dark
Наверное, я ответил на свой вопрос. Сожалею. Я оставлю это на всякий случай, если есть более элегантные решения. Это намного чище без лишних реагирующих крючков, так что пока подойдет. Код песочницы работает:)