Я использую graphql/apollo и реагирую.
У меня есть следующий код
const [state, setState] = useState(undefined);
useEffect(() => {
(async () => {
try {
const workspace = await getFirstWorkspace();
// Do Something
setState(withSomething)
} catch (error) {
// Do Something Else
setState(withErrorSomething)
}
})();
}, [generateLink, getFirstWorkspace, masterDataStoreId]);
теперь это работало нормально, пока я не обновил некоторые пакеты, в настоящее время я получаю эту ошибку.
Uncaught (в обещании) DOMException: сигнал прерывается без причины
Насколько я понимаю, мой useEffect выдает это, когда компонент размонтирован, а запрос не завершен.
Теперь это приводит к тому, что мой улов всегда срабатывает хотя бы один раз, потому что похоже, что когда эффект запускается снова, потому что одно из изменений изменилось, оно терпит неудачу.
Я """ исправил """ это, выполнив
const [state, setState] = useState(undefined);
useEffect(() => {
(async () => {
try {
const workspace = await getFirstWorkspace();
// Do Something
setState(withSomething)
} catch (error) {
// Do Something Else
if ((error as any)?.name === 'AbortError') {
return;
}
setState(withErrorSomething)
}
})();
}, [generateLink, getFirstWorkspace, masterDataStoreId]);
И не назначать какое-либо состояние в случае, если ошибка прерывается. Но я не смог найти подходящего решения или не понимаю, почему это было проблематично раньше, а не сейчас, я обновил какой-то пакет, но ни один не упомянул об изменении поведения на этом конце.
Мой вопрос, что я должен сделать, чтобы сделать все правильно?
удаление обещания из эффекта удалить эту ошибку. В основном все useEffect, содержащие асинхронную операцию, теперь вызывают эту ошибку при изменении страницы. Это может быть связано с каким-то graphql
обновлением, или router
обновлением, или useEffect
ими самими. Но я не смог найти хорошего решения для работы с этим, и возврат graphql и реакция маршрутизатора dom на предыдущие версии по-прежнему вызывают эту ошибку, поэтому я действительно не понимаю, откуда она взялась.
Я не думаю, что указанная вами ошибка исходит от React. Раньше React жаловался, если вы обновили состояние в компоненте, который больше не был смонтирован, но сообщение об ошибке, которое он использовал, было «Невозможно выполнить обновление состояния React для несмонтированного компонента. Это не работает, но указывает утечка памяти в вашем приложении». Но последние версии React этого не делают, потому что команда React решила, что это слишком суетливо.
Тем не менее, отвечая на заданный вопрос:
Если getFirstWorkspace
предлагает способ отменить то, что он делает, вы бы это сделали. Например, если он поддерживает AbortSignal
, вы можете сделать это:
useEffect(() => {
// *** Create a controller and get its signal
const controller = new AbortController();
const { signal } = controller;
(async () => {
try {
// *** Pass the signal to `getFirstWorkspace`
const workspace = await getFirstWorkspace(signal);
// *** Only do something if the signal isn't aborted
if (!signal.aborted) {
// Do Something
setState(withSomething);
}
} catch (error) {
// *** Only do something if the signal isn't aborted
if (!signal.aborted) {
// Do Something Else
setState(withErrorSomething);
}
}
})();
return () => {
// *** Abort the signal on cleanup
controller.abort();
};
}, [generateLink, getFirstWorkspace, masterDataStoreId]);
... или аналогичный, если он не поддерживает специально AbortSignal
, но предоставляет какой-либо другой способ отмены своей работы.
Если это не так, вы можете вернуться к флагу, говорящему вам не использовать результат:
useEffect(() => {
// *** Start with a flag set to `false`
let cancelled = false;
(async () => {
try {
const workspace = await getFirstWorkspace();
// *** Only do something if the flag is still `false`
if (!cancelled) {
// Do Something
setState(withSomething);
}
} catch (error) {
// *** Only do something if the flag is still `false`
if (!cancelled) {
// Do Something Else
setState(withErrorSomething);
}
}
})();
return () => {
// *** Set the flag on cleanup
cancelled = true;
};
}, [generateLink, getFirstWorkspace, masterDataStoreId]);
На самом деле лучше отменить работу, если вы можете, но вполне нормально иметь резервное логическое значение, если вы не можете. Только не думайте, что вы не можете, обязательно сначала проверьте. :-)
Примечание: я люблю async
/await
, но когда вы делаете всего один вызов и получаете обещание, создание оболочки async
и try
/catch
вокруг await
может быть немного излишним. FWIW, просто прямое использование обещания выглядит так (в этом случае используется флаг, но он работает так же хорошо с контроллером/сигналом):
useEffect(() => {
let cancelled = false;
getFirstWorkspace().then(
(workspace) => {
if (!cancelled) {
// Do Something
setState(withSomething);
}
},
(error) => {
if (!cancelled) {
// Do Something Else
setState(withErrorSomething);
}
}
);
return () => {
cancelled = true;
};
}, [generateLink, getFirstWorkspace, masterDataStoreId]);
Вы могли бы использовать AbortController
const [state, setState] = useState(undefined);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
(async () => {
try {
const workspace = await getFirstWorkspace(signal);
// Do Something
setState(withSomething)
} catch (error) {
// Do Something Else
setState(withErrorSomething)
}
})();
return =()=>Controller.abort();
}, [generateLink, getFirstWorkspace, masterDataStoreId]);
«Насколько я понимаю, мой useEffect выдает это, когда компонент размонтирован, а выполнение запроса не завершено». Что заставляет вас думать, что? У вас есть ссылка на это? Я никогда не видел эту ошибку от React. Похоже на что-то другое.