diff --git a/docs/useBattery.md b/docs/useBattery.md
index 4ddbcc1e..32498d86 100644
--- a/docs/useBattery.md
+++ b/docs/useBattery.md
@@ -2,6 +2,9 @@
React sensor hook that tracks battery status.
+>**Note:** current `BatteryManager` API state is obsolete.
+>Although it may still work in some browsers, its use is discouraged since it could be removed at any time.
+
## Usage
@@ -9,12 +12,47 @@ React sensor hook that tracks battery status.
import {useBattery} from 'react-use';
const Demo = () => {
- const state = useBattery();
+ const batteryState = useBattery();
+
+ if (!batteryState.isSupported) {
+ return (
+
+ Battery sensor: not supported
+
+ );
+ }
+
+ if (!batteryState.fetched) {
+ return (
+
+ Battery sensor: supported
+ Battery state: fetching
+
+ );
+ }
return (
-
- {JSON.stringify(state, null, 2)}
-
+
+ Battery sensor: supported
+ Battery state: fetched
+ Charge level: { (batteryState.level * 100).toFixed(0) }%
+ Charging: { batteryState.charging ? 'yes' : 'no' }
+ Charging time:
+ { batteryState.chargingTime ? batteryState.chargingTime : 'finished' }
+ Discharging time: { batteryState.dischargingTime }
+
);
};
```
+
+## Reference
+
+```ts
+const {isSupported, level, charging, dischargingTime, chargingTime} = useBattery();
+```
+- **`isSupported`**_`: boolean`_ - whether browser/devise supports BatteryManager;
+- **`fetched`**_`: boolean`_ - whether battery state is fetched;
+- **`level`**_`: number`_ - representing the system's battery charge level scaled to a value between 0.0 and 1.0.
+- **`charging`**_`: boolean`_ - indicating whether or not the battery is currently being charged.
+- **`dischargingTime`**_`: number`_ - remaining time in seconds until the battery is completely discharged and the system will suspend.
+- **`chargingTime`**_`: number`_ - remaining time in seconds until the battery is fully charged, or 0 if the battery is already fully charged.
diff --git a/src/__stories__/useBattery.story.tsx b/src/__stories__/useBattery.story.tsx
index 5c6ae635..9a38321a 100644
--- a/src/__stories__/useBattery.story.tsx
+++ b/src/__stories__/useBattery.story.tsx
@@ -4,9 +4,36 @@ import { useBattery } from '..';
import ShowDocs from './util/ShowDocs';
const Demo = () => {
- const state = useBattery();
+ const batteryState = useBattery();
- return {JSON.stringify(state, null, 2)};
+ if (!batteryState.isSupported) {
+ return (
+
+ Battery sensor: not supported
+
+ );
+ }
+
+ if (!batteryState.fetched) {
+ return (
+
+ Battery sensor: supported
+ Battery state: fetching
+
+ );
+ }
+
+ return (
+
+ Battery sensor: supported
+ Battery state: fetched
+ Charge level: {(batteryState.level * 100).toFixed(0)}%
+ Charging: {batteryState.charging ? 'yes' : 'no'}
+ Charging time:
+ {batteryState.chargingTime ? batteryState.chargingTime : 'finished'}
+ Discharging time: {batteryState.dischargingTime}
+
+ );
};
storiesOf('Sensors|useBattery', module)
diff --git a/src/useBattery.ts b/src/useBattery.ts
index a5cfd5c2..2b22b6d1 100644
--- a/src/useBattery.ts
+++ b/src/useBattery.ts
@@ -1,56 +1,85 @@
-import { useEffect, useState } from 'react';
+import * as React from 'react';
+import * as isEqual from 'react-fast-compare';
import { off, on } from './util';
-export interface BatterySensorState {
+const { useState, useEffect } = React;
+
+export interface BatteryState {
charging: boolean;
- level: number;
chargingTime: number;
dischargingTime: number;
+ level: number;
}
-const useBattery = () => {
- const [state, setState] = useState({});
- let mounted = true;
- let battery: any = null;
+interface BatteryManager extends Readonly, EventTarget {
+ onchargingchange: () => void;
+ onchargingtimechange: () => void;
+ ondischargingtimechange: () => void;
+ onlevelchange: () => void;
+}
- const onChange = () => {
- const { charging, level, chargingTime, dischargingTime } = battery;
- setState({
- charging,
- level,
- chargingTime,
- dischargingTime,
- });
- };
+interface NavigatorWithPossibleBattery extends Navigator {
+ getBattery?: () => Promise;
+}
- const onBattery = () => {
- onChange();
- on(battery, 'chargingchange', onChange);
- on(battery, 'levelchange', onChange);
- on(battery, 'chargingtimechange', onChange);
- on(battery, 'dischargingtimechange', onChange);
- };
+type UseBatteryState =
+ | { isSupported: false } // Battery API is not supported
+ | { isSupported: true; fetched: false } // battery API supported but not fetched yet
+ | BatteryState & { isSupported: true; fetched: true }; // battery API supported and fetched
+
+const nav: NavigatorWithPossibleBattery | undefined = typeof navigator === 'object' ? navigator : undefined;
+const isBatteryApiSupported = nav && typeof nav.getBattery === 'function';
+
+function useBatteryMock(): UseBatteryState {
+ return { isSupported: false };
+}
+
+function useBattery(): UseBatteryState {
+ const [state, setState] = useState({ isSupported: true, fetched: false });
useEffect(() => {
- (navigator as any).getBattery().then((bat: any) => {
- if (mounted) {
- battery = bat;
- onBattery();
+ let isMounted = true;
+ let battery: BatteryManager | null = null;
+
+ const handleChange = () => {
+ if (!isMounted || !battery) {
+ return;
}
+ const newState: UseBatteryState = {
+ isSupported: true,
+ fetched: true,
+ level: battery.level,
+ charging: battery.charging,
+ dischargingTime: battery.dischargingTime,
+ chargingTime: battery.chargingTime,
+ };
+ !isEqual(state, newState) && setState(newState);
+ };
+
+ nav!.getBattery!().then((bat: BatteryManager) => {
+ if (!isMounted) {
+ return;
+ }
+ battery = bat;
+ on(battery, 'chargingchange', handleChange);
+ on(battery, 'chargingtimechange', handleChange);
+ on(battery, 'dischargingtimechange', handleChange);
+ on(battery, 'levelchange', handleChange);
+ handleChange();
});
return () => {
- mounted = false;
+ isMounted = false;
if (battery) {
- off(battery, 'chargingchange', onChange);
- off(battery, 'levelchange', onChange);
- off(battery, 'chargingtimechange', onChange);
- off(battery, 'dischargingtimechange', onChange);
+ off(battery, 'chargingchange', handleChange);
+ off(battery, 'chargingtimechange', handleChange);
+ off(battery, 'dischargingtimechange', handleChange);
+ off(battery, 'levelchange', handleChange);
}
};
}, []);
return state;
-};
+}
-export default useBattery;
+export default isBatteryApiSupported ? useBattery : useBatteryMock;