80 lines
2.3 KiB
TypeScript
80 lines
2.3 KiB
TypeScript
import { useEffect, useRef, useState } from "react";
|
||
import { AppState, AppStateStatus, Platform } from "react-native";
|
||
|
||
type Control = {
|
||
start: () => void;
|
||
stop: () => void;
|
||
};
|
||
|
||
type State = "RUNNING" | "STOPPED";
|
||
|
||
type Fn = () => void;
|
||
|
||
export const useInterval = (fn: Fn, interval: number, autostart = true) => {
|
||
const onUpdateRef = useRef<Fn>();
|
||
const [state, setState] = useState("RUNNING");
|
||
// ユーザー操作によるSTOP(AppStateによる一時停止と区別する)
|
||
const userStoppedRef = useRef(!autostart);
|
||
|
||
const start = () => {
|
||
userStoppedRef.current = false;
|
||
setState("RUNNING");
|
||
};
|
||
const stop = () => {
|
||
userStoppedRef.current = true;
|
||
setState("STOPPED");
|
||
};
|
||
useEffect(() => {
|
||
onUpdateRef.current = fn;
|
||
}, [fn]);
|
||
useEffect(() => {
|
||
if (autostart) {
|
||
userStoppedRef.current = false;
|
||
setState("RUNNING");
|
||
} else {
|
||
userStoppedRef.current = true;
|
||
setState("STOPPED");
|
||
}
|
||
}, [autostart]);
|
||
|
||
// バックグラウンド移行時に停止、フォアグラウンド復帰時に即時実行して再開
|
||
useEffect(() => {
|
||
if (Platform.OS === "web") return;
|
||
const handleAppStateChange = (nextAppState: AppStateStatus) => {
|
||
if (nextAppState === "active") {
|
||
if (!userStoppedRef.current) {
|
||
// 復帰直後に即時フェッチして最新データを取得
|
||
onUpdateRef.current?.();
|
||
setState("RUNNING");
|
||
}
|
||
} else if (nextAppState === "background" || nextAppState === "inactive") {
|
||
if (!userStoppedRef.current) {
|
||
// バックグラウンド中はインターバルを停止してムダなfetchエラーを防ぐ
|
||
setState("STOPPED");
|
||
}
|
||
}
|
||
};
|
||
const subscription = AppState.addEventListener("change", handleAppStateChange);
|
||
return () => {
|
||
subscription.remove();
|
||
};
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
let timerId: ReturnType<typeof setInterval> | undefined;
|
||
if (state === "RUNNING") {
|
||
timerId = setInterval(() => {
|
||
onUpdateRef.current?.();
|
||
}, interval);
|
||
} else {
|
||
if (timerId) clearInterval(timerId);
|
||
}
|
||
return () => {
|
||
if (timerId) clearInterval(timerId);
|
||
};
|
||
}, [interval, state]);
|
||
return [state, { start, stop }];
|
||
};
|
||
|
||
export default useInterval;
|