私のようなものがあります:
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フックを設定する必要があるということですか?
ReactクラスコンポーネントでsetStateが行うように、状態更新が行われた後、useState
setterはコールバックを提供しません。同じ振る舞いを再現するために、ReactクラスコンポーネントのcomponentDidUpdate
ライフサイクルメソッドのような類似のパターンを、フックを使用してuseEffect
と使用できます。
useEffect
フックは、2番目のパラメーターを値の配列として受け取ります。Reactは、レンダリングサイクルの完了後に変更を監視する必要があります。
const [loading, setLoading] = useState(false);
...
useEffect(() => {
doSomething(); // This is be executed when `loading` state changes
}, [loading])
setLoading(true);
編集
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);
};
コンポーネントが再レンダリングされるまで待ちます。
const [loading, setLoading] = useState(false);
useEffect(() => {
if (loading) {
doSomething();
}
}, [loading]);
setLoading(true);
次のようなもので明瞭さを改善できます:
function doSomething() {
// your side effects
// return () => { }
}
function useEffectIf(condition, fn) {
useEffect(() => condition && fn(), [condition])
}
function App() {
const [loading, setLoading] = useState(false);
useEffectIf(loading, doSomething)
return (
<>
<div>{loading}</div>
<button onClick={() => setLoading(true)}>Click Me</button>
</>
);
}
ここでのユースケースが正確に何であるかはわかりませんが、カスタムフックを作成することで非同期アクションを達成できると思います。
以下は参考用です
const useAPI =(initState)=>{
const [custState,changeCustState] = useState(initState);
return ([custState,(newState,accept a callback here )=>{
//console.log(newState);
/// execute the call back ()
return changeCustState(newState);}]);
};
...
const [loading, setLoading] = useAPI(false);