[解決済み] react-hooksによるステート更新時の非同期コードの実行
2022-05-17 15:03:13
質問
以下のようなものがあります。
const [loading, setLoading] = useState(false);
...
setLoading(true);
doSomething(); // <--- when here, loading is still false.
状態を設定するのはやはり非同期なので、これを待つにはどうしたらいいのか
setLoading()
の呼び出しが終了するのを待つのに最適な方法は何でしょうか?
は
setLoading()
のようなコールバックは受け付けないようです。
setState()
のようなコールバックは受け付けないようです。
一例
クラスベース
getNextPage = () => {
// This will scroll back to the top, and also trigger the prefetch for the next page on the way up.
goToTop();
if (this.state.pagesSeen.includes(this.state.page + 1)) {
return this.setState({
page: this.state.page + 1,
});
}
if (this.state.prefetchedOrders) {
const allOrders = this.state.orders.concat(this.state.prefetchedOrders);
return this.setState({
orders: allOrders,
page: this.state.page + 1,
pagesSeen: [...this.state.pagesSeen, this.state.page + 1],
prefetchedOrders: null,
});
}
this.setState(
{
isLoading: true,
},
() => {
getOrders({
page: this.state.page + 1,
query: this.state.query,
held: this.state.holdMode,
statuses: filterMap[this.state.filterBy],
})
.then((o) => {
const { orders } = o.data;
const allOrders = this.state.orders.concat(orders);
this.setState({
orders: allOrders,
isLoading: false,
page: this.state.page + 1,
pagesSeen: [...this.state.pagesSeen, this.state.page + 1],
// Just in case we're in the middle of a prefetch.
prefetchedOrders: null,
});
})
.catch(e => console.error(e.message));
},
);
};
関数型に変換する
const getNextPage = () => {
// This will scroll back to the top, and also trigger the prefetch for the next page on the way up.
goToTop();
if (pagesSeen.includes(page + 1)) {
return setPage(page + 1);
}
if (prefetchedOrders) {
const allOrders = orders.concat(prefetchedOrders);
setOrders(allOrders);
setPage(page + 1);
setPagesSeen([...pagesSeen, page + 1]);
setPrefetchedOrders(null);
return;
}
setIsLoading(true);
getOrders({
page: page + 1,
query: localQuery,
held: localHoldMode,
statuses: filterMap[filterBy],
})
.then((o) => {
const { orders: fetchedOrders } = o.data;
const allOrders = orders.concat(fetchedOrders);
setOrders(allOrders);
setPage(page + 1);
setPagesSeen([...pagesSeen, page + 1]);
setPrefetchedOrders(null);
setIsLoading(false);
})
.catch(e => console.error(e.message));
};
上記では、各setWhateverの呼び出しを順次実行したいと思います。これは、この動作を再現するために、多くの異なる useEffect フックを設定する必要があるということでしょうか。
どのように解決するのですか?
useState
setStateがReactクラスのコンポーネントで行うような、状態の更新が行われた後のコールバックは提供されません。同じ挙動を再現するために、以下のような類似のパターンを利用することができます。
componentDidUpdate
という React クラスコンポーネントのライフサイクルメソッドを利用することができます。
useEffect
Hooksを使用する
useEffect
フックは、レンダリングサイクルが完了した後に、React が変更を監視する必要がある値の配列として、2 番目のパラメータを受け取ります。
const [loading, setLoading] = useState(false);
...
useEffect(() => {
doSomething(); // This is be executed when `loading` state changes
}, [loading])
setLoading(true);
EDIT
とは異なり
setState
のアップデータは
useState
フックにはコールバックがありませんが、常に
useEffect
を使えば上記の挙動を再現できます。ただし、読み込みの変化を判断する必要があります
あなたのコードに対する機能的なアプローチは次のようになります。
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
const prevLoading = usePrevious(isLoading);
useEffect(() => {
if (!prevLoading && isLoading) {
getOrders({
page: page + 1,
query: localQuery,
held: localHoldMode,
statuses: filterMap[filterBy],
})
.then((o) => {
const { orders: fetchedOrders } = o.data;
const allOrders = orders.concat(fetchedOrders);
setOrders(allOrders);
setPage(page + 1);
setPagesSeen([...pagesSeen, page + 1]);
setPrefetchedOrders(null);
setIsLoading(false);
})
.catch(e => console.error(e.message));
}
}, [isLoading, preFetchedOrders, orders, page, pagesSeen]);
const getNextPage = () => {
// This will scroll back to the top, and also trigger the prefetch for the next page on the way up.
goToTop();
if (pagesSeen.includes(page + 1)) {
return setPage(page + 1);
}
if (prefetchedOrders) {
const allOrders = orders.concat(prefetchedOrders);
setOrders(allOrders);
setPage(page + 1);
setPagesSeen([...pagesSeen, page + 1]);
setPrefetchedOrders(null);
return;
}
setIsLoading(true);
};
関連
-
[解決済み] forEachループでasync/awaitを使用する
-
[解決済み] Reactのstateとpropsの違いとは?
-
[解決済み] Reactでネストした状態のプロパティを更新する方法
-
[解決済み] Reactで親の状態を更新するにはどうしたらいいですか?
-
[解決済み] Reactの "after render "コード?
-
[解決済み] React HooksでcomponentWillMount()を使用するには?
-
[解決済み】React:setStateを使用してstateのstate.item[1]を更新するには?
-
[解決済み】React Hooks useEffectでoldValuesとnewValuesを比較する方法は?
-
[解決済み] WebpackでjQueryを本物のWindowオブジェクトに公開する
-
[解決済み] jQueryを使って、ターゲット要素のクリック座標を取得する方法
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] AngularJSのエラーです。Cross Origin リクエストはプロトコルスキーム http, data, chrome-extension, https に対してのみサポートされています。
-
[解決済み] URL/アドレスバーからJavascriptの関数を呼び出す
-
[解決済み] express.json()とexpress.urlencoded()とは何ですか?
-
[解決済み] バックボーンビュー。親からイベントを継承・拡張する
-
[解決済み] Facebook Reactでコードを再利用するためのミキシンとコンポーネントの使い分け
-
[解決済み] jqueryで部分文字列を作成する方法
-
[解決済み] jQueryを使って、ターゲット要素のクリック座標を取得する方法
-
[解決済み] JavaScript 予期せぬ事態に対する可能な反復処理
-
[解決済み] Math.random()を呼び出す関数は純粋か?
-
[解決済み] Firefox で ES2015 のインポートが機能しない(トップレベルでも)。