В моем компоненте реакции я пытаюсь реализовать простой счетчик, пока выполняется запрос ajax - я использую состояние для хранения статуса загрузки.
По какой-то причине этот фрагмент кода ниже в моем компоненте React выдает эту ошибку
Можно обновить только смонтированный или монтируемый компонент. Обычно это означает, что вы вызвали setState () для отключенного компонента. Это запретная игра. Пожалуйста, проверьте код неопределенного компонента.
Если я избавлюсь от первого вызова setState, ошибка исчезнет.
constructor(props) {
super(props);
this.loadSearches = this.loadSearches.bind(this);
this.state = {
loading: false
}
}
loadSearches() {
this.setState({
loading: true,
searches: []
});
console.log('Loading Searches..');
$.ajax({
url: this.props.source + '?projectId=' + this.props.projectId,
dataType: 'json',
crossDomain: true,
success: function(data) {
this.setState({
loading: false
});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
this.setState({
loading: false
});
}.bind(this)
});
}
componentDidMount() {
setInterval(this.loadSearches, this.props.pollInterval);
}
render() {
let searches = this.state.searches || [];
return (<div>
<Table striped bordered condensed hover>
<thead>
<tr>
<th>Name</th>
<th>Submit Date</th>
<th>Dataset & Datatype</th>
<th>Results</th>
<th>Last Downloaded</th>
</tr>
</thead>
{
searches.map(function(search) {
let createdDate = moment(search.createdDate, 'X').format("YYYY-MM-DD");
let downloadedDate = moment(search.downloadedDate, 'X').format("YYYY-MM-DD");
let records = 0;
let status = search.status ? search.status.toLowerCase() : ''
return (
<tbody key={search.id}>
<tr>
<td>{search.name}</td>
<td>{createdDate}</td>
<td>{search.dataset}</td>
<td>{records}</td>
<td>{downloadedDate}</td>
</tr>
</tbody>
);
}
</Table >
</div>
);
}
Вопрос в том, почему я получаю эту ошибку, когда компонент уже должен быть смонтирован (поскольку он вызывается из componentDidMount). Я думал, что можно безопасно установить состояние после монтирования компонента?
javascript
ajax
reactjs
state
Марти
источник
источник
this.state = { loading : null };
Ответы:
Не увидеть функцию рендеринга немного сложно. Хотя вы уже можете определить, что вам нужно сделать, каждый раз, когда вы используете интервал, вам нужно очищать его при размонтировании. Так:
componentDidMount() { this.loadInterval = setInterval(this.loadSearches, this.props.pollInterval); } componentWillUnmount () { this.loadInterval && clearInterval(this.loadInterval); this.loadInterval = false; }
Поскольку эти обратные вызовы успеха и ошибки могут по-прежнему вызываться после размонтирования, вы можете использовать переменную интервала, чтобы проверить, смонтирован ли он.
this.loadInterval && this.setState({ loading: false });
Надеюсь, это поможет, предоставьте функцию рендеринга, если это не работает.
Ура
источник
componentWillUnmount() { clearInterval(this.loadInterval); }
Это не вызывается из
componentDidMount
. ВыcomponentDidMount
создаете функцию обратного вызова, которая будет выполняться в стеке обработчика таймера, а не в стекеcomponentDidMount
. Очевидно, к моменту выполнения вашего callback (this.loadSearches
) компонент уже отключился.Так что принятый ответ защитит вас. Если вы используете какой-либо другой асинхронный API, который не позволяет вам отменять асинхронные функции (уже отправленные какому-либо обработчику), вы можете сделать следующее:
if (this.isMounted()) this.setState(...
Это избавит вас от сообщения об ошибке, о котором вы сообщаете во всех случаях, хотя это действительно похоже на подметание вещей, особенно если ваш API предоставляет возможность отмены (как и в
setInterval
случае сclearInterval
).источник
isMounted
- это антипаттерн, который facebook советует не использовать: facebook.github.io/react/blog/2015/12/16/…Для кого нужен другой вариант, метод обратного вызова атрибута ref может быть обходным решением. Параметр handleRef - это ссылка на элемент DOM div.
Для получения подробной информации о refs и DOM: https://facebook.github.io/react/docs/refs-and-the-dom.html
handleRef = (divElement) => { if(divElement){ //set state here } } render(){ return ( <div ref={this.handleRef}> </div> ) }
источник
class myClass extends Component { _isMounted = false; constructor(props) { super(props); this.state = { data: [], }; } componentDidMount() { this._isMounted = true; this._getData(); } componentWillUnmount() { this._isMounted = false; } _getData() { axios.get('https://example.com') .then(data => { if (this._isMounted) { this.setState({ data }) } }); } render() { ... } }
источник
Для потомков,
Эта ошибка в нашем случае была связана с Reflux, обратными вызовами, перенаправлениями и setState. Мы отправили setState в обратный вызов onDone, но мы также отправили перенаправление в обратный вызов onSuccess. В случае успеха наш обратный вызов onSuccess выполняется до onDone . Это вызывает перенаправление до попытки setState . Таким образом, ошибка setState на отключенном компоненте.
Действие магазина рефлюкса:
generateWorkflow: function( workflowTemplate, trackingNumber, done, onSuccess, onFail) {...
Позвоните перед исправлением:
Actions.generateWorkflow( values.workflowTemplate, values.number, this.setLoading.bind(this, false), this.successRedirect );
Звоните после исправления:
Actions.generateWorkflow( values.workflowTemplate, values.number, null, this.successRedirect, this.setLoading.bind(this, false) );
Больше
В некоторых случаях, поскольку isMounted в React является «устаревшим / анти-паттерном», мы приняли использование переменной _mounted и сами отслеживаем ее.
источник
Поделитесь решением с помощью обработчиков реакции .
React.useEffect(() => { let isSubscribed = true callApi(...) .catch(err => isSubscribed ? this.setState(...) : Promise.reject({ isSubscribed, ...err })) .then(res => isSubscribed ? this.setState(...) : Promise.reject({ isSubscribed })) .catch(({ isSubscribed, ...err }) => console.error('request cancelled:', !isSubscribed)) return () => (isSubscribed = false) }, [])
то же решение можно распространить на случаи, когда вы хотите отменить предыдущие запросы при изменении идентификатора выборки, в противном случае возникли бы условия гонки между несколькими запросами в полете (
this.setState
вызываемые не по порядку).React.useEffect(() => { let isCancelled = false callApi(id).then(...).catch(...) // similar to above return () => (isCancelled = true) }, [id])
это работает благодаря закрытию в javascript.
В целом, идея выше была близка к подходу makeCancelable, рекомендованному в React doc, в котором четко указано
Кредит
https://juliangaramendy.dev/use-promise-subscription/
источник