W moim komponencie reagowania próbuję zaimplementować prosty spinner, gdy trwa żądanie Ajax - używam stanu do przechowywania statusu ładowania.
Z jakiegoś powodu ten fragment kodu poniżej w moim komponencie React zgłasza ten błąd
Może aktualizować tylko zamontowany lub montowany komponent. Zwykle oznacza to, że wywołałeś setState () na niezmontowanym komponencie. To nie jest operacja. Sprawdź kod niezdefiniowanego komponentu.
Jeśli pozbędę się pierwszego wywołania setState, błąd zniknie.
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>
);
}
Pytanie brzmi, dlaczego otrzymuję ten błąd, gdy komponent powinien być już zamontowany (jak jest wywoływany z componentDidMount). Myślałem, że można bezpiecznie ustawić stan po zamontowaniu komponentu?
javascript
ajax
reactjs
state
Marty
źródło
źródło
this.state = { loading : null };
Odpowiedzi:
Bez zobaczenia funkcji renderowania jest trochę trudne. Chociaż możesz już dostrzec coś, co powinieneś zrobić, za każdym razem, gdy używasz interwału, musisz go wyczyścić przy odmontowaniu. Więc:
componentDidMount() { this.loadInterval = setInterval(this.loadSearches, this.props.pollInterval); } componentWillUnmount () { this.loadInterval && clearInterval(this.loadInterval); this.loadInterval = false; }
Ponieważ te wywołania zwrotne sukcesu i błędu mogą nadal być wywoływane po odmontowaniu, możesz użyć zmiennej interwału, aby sprawdzić, czy jest zamontowana.
this.loadInterval && this.setState({ loading: false });
Mam nadzieję, że to pomoże, zapewnij funkcję renderowania, jeśli to nie zadziała.
Twoje zdrowie
źródło
componentWillUnmount() { clearInterval(this.loadInterval); }
To jest nie wywoływana z
componentDidMount
. TwojecomponentDidMount
ikra funkcji zwrotnej, która zostanie wykonana w stosie obsługi timera, a nie w stosiecomponentDidMount
. Najwyraźniej do czasu wykonania funkcji callback (this.loadSearches
) komponent został odmontowany.Tak więc przyjęta odpowiedź cię ochroni. Jeśli używasz innego asynchronicznego interfejsu API, który nie pozwala na anulowanie funkcji asynchronicznych (już przesłanych do jakiejś obsługi), możesz wykonać następujące czynności:
if (this.isMounted()) this.setState(...
Pozwoli to usunąć komunikat o błędzie, który zgłaszasz we wszystkich przypadkach, chociaż wydaje się, że zamiatanie rzeczy pod dywanikiem, szczególnie jeśli twoje API zapewnia możliwość anulowania (tak
setInterval
jak w przypadkuclearInterval
).źródło
isMounted
jest antywzorzec projektowy, że Facebook radzi nie używać: facebook.github.io/react/blog/2015/12/16/...Kto potrzebuje innej opcji, metoda wywołania zwrotnego atrybutu ref może być obejściem. Parametr handleRef jest odniesieniem do elementu div DOM.
Szczegółowe informacje na temat referencji i 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> ) }
źródło
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() { ... } }
źródło
Dla potomnych,
Ten błąd w naszym przypadku był związany z refluksem, callbackami, przekierowaniami i setState. Wysłaliśmy setState do wywołania zwrotnego onDone, ale wysłaliśmy również przekierowanie do wywołania zwrotnego onSuccess. W przypadku powodzenia nasze wywołanie zwrotne onSuccess jest wykonywane przed onDone . To powoduje przekierowanie przed próbą setstate . Tak więc błąd, setState na niezmontowanym komponencie.
Akcja sklepu Reflux:
generateWorkflow: function( workflowTemplate, trackingNumber, done, onSuccess, onFail) {...
Zadzwoń przed naprawą:
Actions.generateWorkflow( values.workflowTemplate, values.number, this.setLoading.bind(this, false), this.successRedirect );
Zadzwoń po naprawie:
Actions.generateWorkflow( values.workflowTemplate, values.number, null, this.successRedirect, this.setLoading.bind(this, false) );
Jeszcze
W niektórych przypadkach, ponieważ isMounted Reacta jest "przestarzałe / anty-wzorzec", przyjęliśmy użycie zmiennej _mounted i sami ją monitorowaliśmy.
źródło
Udostępnij rozwiązanie, które umożliwia reagowanie hookami .
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) }, [])
to samo rozwiązanie można rozszerzyć na dowolne miejsce, w którym chcesz anulować poprzednie żądania przy zmianie identyfikatora pobierania, w przeciwnym razie wystąpią warunki wyścigu między wieloma żądaniami w locie (
this.setState
wywołanymi poza kolejnością).React.useEffect(() => { let isCancelled = false callApi(id).then(...).catch(...) // similar to above return () => (isCancelled = true) }, [id])
działa to dzięki domknięciom w javascript.
Ogólnie rzecz biorąc, powyższy pomysł był zbliżony do podejścia makeCancelable zalecanego w dokumencie react doc, które jasno stwierdza
Kredyt
https://juliangaramendy.dev/use-promise-subscription/
źródło