私のreactコンポーネントでは、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から呼び出されるため)
レンダリング機能を見ることは少し難しいです。すでに行うべきことを見つけることができますが、間隔を使用するたびに、アンマウント時にクリアする必要があります。そう:
componentDidMount() {
this.loadInterval = setInterval(this.loadSearches, this.props.pollInterval);
}
componentWillUnmount () {
this.loadInterval && clearInterval(this.loadInterval);
this.loadInterval = false;
}
これらの成功およびエラーコールバックは、アンマウント後に呼び出される可能性があるため、interval変数を使用して、マウントされているかどうかを確認できます。
this.loadInterval && this.setState({
loading: false
});
これが役に立てば、これが役に立たない場合はレンダリング機能を提供します。
乾杯
質問は、コンポーネントが既にマウントされているときにこのエラーが発生するのはなぜですか(componentDidMountから呼び出されるため)
それはnotcomponentDidMount
から呼び出されます。 componentDidMount
は、componentDidMount
のスタックではなく、タイマーハンドラーのスタックで実行されるコールバック関数を生成します。明らかに、コールバック(this.loadSearches
)が実行されるまでに、コンポーネントはアンマウントされています。
したがって、受け入れられた答えはあなたを保護します。 (既にハンドラーに送信済みの)非同期関数をキャンセルできない他の非同期APIを使用している場合、次のことができます。
if (this.isMounted())
this.setState(...
これは、特にAPIがキャンセル機能を提供している場合(setInterval
がclearInterval
で行うように)ラグの下に物を一掃するように感じますが、すべての場合に報告するエラーメッセージを取り除きます。
別のオプションが必要な場合は、ref属性のコールバックメソッドが回避策になります。 handleRefのパラメーターは、div DOM要素への参照です。
Refと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)
);
場合によっては、ReactのisMountedは「非推奨/アンチパターン」であるため、_mounted変数の使用を採用し、それを自分で監視しています。
react hooks によって有効化されたソリューションを共有します。
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)
}, [])
同じソリューションは、フェッチIDの変更に関する以前のリクエストcancelをいつでも拡張できます。 this.setState
が故障しています)。
React.useEffect(() => {
let isCancelled = false
callApi(id).then(...).catch(...) // similar to above
return () => (isCancelled = true)
}, [id])
これは、javascriptの closures のおかげで機能します。
一般に、上記のアイデアは makeCancelableアプローチ に近いもので、react docが推奨しています。
isMountedはアンチパターンです