#reactjs #react-native #setstate
#reactjs #react-native #setstate
Вопрос:
У меня есть эта init
функция, которая вызывается в componentDidMount
или других функциях, которые вызываются при взаимодействии с пользователем:
protected async init() {
if (this.props.requiredSource)
) {
this.setState({ imageSource: this.props.requiredSource }); // task I want to cancel in componentWillUnmount;
return;
}
// ... more code here ....
const exists = await RNFS.exists(uri);
if (exists) {
this.cropImageForDisplayView(uri); // async that needs canceling in componentWillUnmount
this.iAmUploading(); // async that needs canceling
return;
}
this.setState({ imageSource: { uri: httpUri } }); // same here
return;
}
try {
// ...more code here...
this.setState({ imageSource: { uri: `${httpUri.split('?')[0]}?w=${size.w}amp;h=${size.h}amp;min=1` } }); // same here
} catch (e) {
this.setState({ imageSource: { uri: httpUri } }); // same here
}
}
private iAmUploading = async () => {
if (this.props.tempFileId != null) {
this.setState({ tempfileId: this.props.tempFileId }); // same here
}
};
Приведенный выше код нуждается в некотором рефакторинге, поскольку он генерирует некоторые классические update component that doesn't exist
ошибки.
Мне нужен ответ о лучших методах использования контроллера прерывания или отменяемых обещаний для отмены нескольких вызовов setStates и асинхронных задач, когда компонент больше не существует.
Я изучил: https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html
но этой статье 5 лет, мне не очень понятно, является ли это лучшей практикой в наши дни и как я мог бы адаптировать это к отмене нескольких асинхронных задач и вызовов setState.
Любая помощь, которая не включает классический анти-шаблон this.isMounted
, будет высоко оценена.
Примечание: текущее приложение написано с использованием классических компонентов (без перехватов), и рефакторинг в сторону перехватов на данный момент невозможен.
"react": "16.13.1",
"react-dom": "16.11.0",
"react-native": "0.63.3",
Ответ №1:
- Стандартный подход, описанный в нескольких руководствах по реагированию (включая тот, на который дана ссылка в официальных документах react), заключается в установке флага при отключении компонента и проверке этого флага после каждого асинхронного шага вашего кода. Вероятно, это нормально для одного асинхронного вызова, но становится довольно подробным, если у вас есть несколько асинхронных вызовов, которые соединяются один за другим.
- Немного более чистое решение для этих более сложных случаев может быть достигнуто с помощью библиотеки, которая обеспечивает отмену из коробки.
cancelable-promises
Выше илиbluebird
предоставить реализации, совместимые с обещаниями, которые позволяют отменять. - Другим подходом было бы переключение на использование API на основе Observable
rxjs
вместо promise-based. Наблюдаемые отменяются по дизайну. Недостатком является то, что вы не сможете использоватьasync
/await
с ними.
Ответ №2:
Вы можете использовать библиотеку, подобную cancelable-promise, чтобы сделать ваши обещания отменяемыми. Вот так:
import { cancelable } from 'cancelable-promise';
protected async init() {
// ... code here ....
const exists = await RNFS.exists(uri);
if (exists) {
this.cancelableCropImageForDisplayView = cancelable(this.cropImageForDisplayView(uri));
this.cancelableIAmUploading = cancelable(this.iAmUploading());
return;
}
// ... code here ....
}
Затем в componentWillUnmount вам просто нужно отменить свои обещания.
componentWillUnmount() {
this.cancelableCropImageForDisplayView.cancel();
this.cancelableIAmUploading.cancel();
}
Имейте в виду, что ваши обещания все равно будут выполнены, но результат будет отброшен. Если вы действительно хотите отменить выполнение сразу после запуска componentWillUnmount, вам нужно убедиться, что ваши асинхронные вызовы поддерживают какой-то сигнал прерывания (что-то похожее на отмену Axios).
Ответ №3:
Если вы используете axios, вам необходимо сгенерировать токен отмены для каждого запроса, чтобы сделать его отменяемым.
Ссылка: https://codesandbox.io/s/cancel-requests-with-axios-reactjs-forked-6v9rf
Ответ №4:
Я пока не рекомендую следующий подход, потому что библиотека CPromise в настоящее время находится на стадии бета-тестирования, так что это просто для осведомленности.
Когда вы используете CPromise
вместо native для построения цепочек обещаний, все асинхронные задачи, включая вложенные, становятся полностью отменяемыми / прерываемыми — при вызове метода cancel они будут отклонены по особой CanceledError
причине. Внутренние асинхронные задания, построенные на обратных вызовах, также будут завершены, если пользовательский код поддерживает cancel
обработку событий. Если вы используете асинхронные функции ECMA, вам необходимо подписать цепочку обещаний на внешний сигнал AbortController.
Демонстрация использования декораторов
import { async, listen, cancel, timeout } from "c-promise2";
import cpFetch from "cp-fetch";
export class TestComponent extends React.Component {
state = {
text: ""
};
@timeout(5000)
@listen
@async
*componentDidMount() {
console.log("mounted");
const response = yield cpFetch(this.props.url);
// more tasks
this.setState({ text: `json: ${yield response.text()}` });
}
render() {
return <div>{this.state.text}</div>;
}
@cancel()
componentWillUnmount() {
console.log("unmounted");
}
}
Вот пример, как написать отменяемый код (живая демонстрация):