Reworked useBattery hook;

Fix: #308;
This commit is contained in:
xobotyi 2019-08-19 11:09:59 +03:00
parent b93bb0a6ed
commit 310fb6dc55
3 changed files with 147 additions and 52 deletions

View File

@ -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,34 @@ React sensor hook that tracks battery status.
import {useBattery} from 'react-use';
const Demo = () => {
const state = useBattery();
const batteryState = useBattery();
if (!batteryState.isSupported) {
return (
<div>
<strong>Battery sensor</strong>: <pre>not supported</pre>
</div>
);
}
return (
<pre>
{JSON.stringify(state, null, 2)}
</pre>
<div>
<strong>Battery sensor</strong>:&nbsp;&nbsp;<span>supported</span><br />
<strong>Charge level</strong>:&nbsp;&nbsp;<span>{(batteryState.level * 100).toFixed(0)}%</span><br />
<strong>Charging</strong>:&nbsp;&nbsp;<span>{batteryState.charging ? 'yes' : 'no'}</span><br />
<strong>Charging time</strong>:&nbsp;&nbsp;<span>{batteryState.chargingTime ? batteryState.chargingTime : 'finished'}</span><br />
<strong>Discharging time</strong>:&nbsp;&nbsp;<span>{batteryState.dischargingTime}</span>
</div>
);
};
```
## Reference
```ts
const {isSupported, level, charging, dischargingTime, chargingTime} = useBattery();
```
- **`isSupported`**_`: boolean`_ - wheter browser/devise supports BatteryManager;
- **`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.

View File

@ -4,9 +4,29 @@ import { useBattery } from '..';
import ShowDocs from './util/ShowDocs';
const Demo = () => {
const state = useBattery();
const batteryState = useBattery();
return <pre>{JSON.stringify(state, null, 2)}</pre>;
if (!batteryState.isSupported) {
return (
<div>
<strong>Battery sensor</strong>: <pre>not supported</pre>
</div>
);
}
return (
<div>
<strong>Battery sensor</strong>:&nbsp;&nbsp;<span>supported</span>
<br />
<strong>Charge level</strong>:&nbsp;&nbsp;<span>{(batteryState.level * 100).toFixed(0)}%</span>
<br />
<strong>Charging</strong>:&nbsp;&nbsp;<span>{batteryState.charging ? 'yes' : 'no'}</span>
<br />
<strong>Charging time</strong>:&nbsp;&nbsp;
<span>{batteryState.chargingTime ? batteryState.chargingTime : 'finished'}</span>
<br />
<strong>Discharging time</strong>:&nbsp;&nbsp;<span>{batteryState.dischargingTime}</span>
</div>
);
};
storiesOf('Sensors|useBattery', module)

View File

@ -1,56 +1,106 @@
import { useEffect, useState } from 'react';
import * as React from 'react';
import { off, on } from './util';
export interface BatterySensorState {
charging: boolean;
level: number;
chargingTime: number;
dischargingTime: number;
enum BatteryManagerEvents {
levelChange = 'levelchange',
dischargingTimeChange = 'dischargingtimechange',
chargingTimeChange = 'chargingtimechange',
chargingChange = 'chargingchange',
}
const useBattery = () => {
const [state, setState] = useState({});
let mounted = true;
let battery: any = null;
export interface BatteryState {
charging: boolean;
chargingTime: number;
dischargingTime: number;
level: number;
}
const onChange = () => {
const { charging, level, chargingTime, dischargingTime } = battery;
setState({
charging,
level,
chargingTime,
dischargingTime,
});
};
interface BatteryManager extends Readonly<BatteryState>, EventTarget {
onchargingchange: () => void;
onchargingtimechange: () => void;
ondischargingtimechange: () => void;
onlevelchange: () => void;
}
const onBattery = () => {
onChange();
on(battery, 'chargingchange', onChange);
on(battery, 'levelchange', onChange);
on(battery, 'chargingtimechange', onChange);
on(battery, 'dischargingtimechange', onChange);
};
interface NavigatorWithPossibleBattery extends Navigator {
getBattery?: () => Promise<BatteryManager>;
}
useEffect(() => {
(navigator as any).getBattery().then((bat: any) => {
if (mounted) {
battery = bat;
onBattery();
}
});
const nav: NavigatorWithPossibleBattery = navigator;
return () => {
mounted = false;
if (battery) {
off(battery, 'chargingchange', onChange);
off(battery, 'levelchange', onChange);
off(battery, 'chargingtimechange', onChange);
off(battery, 'dischargingtimechange', onChange);
}
};
}, []);
return state;
type UseBatteryState = BatteryState & {
isSupported: boolean;
};
export default useBattery;
export default function useBattery() {
const [state, setState] = React.useState<UseBatteryState>({
isSupported: nav && typeof nav.getBattery !== 'undefined',
level: 1,
charging: true,
dischargingTime: Infinity,
chargingTime: 0,
});
const battery = React.useRef<BatteryManager>();
let isMounted = true;
if (state.isSupported) {
const onChange = React.useCallback(() => {
if (isMounted && battery.current) {
setState({
isSupported: true,
level: battery.current.level,
charging: battery.current.charging,
dischargingTime: battery.current.dischargingTime,
chargingTime: battery.current.chargingTime,
});
}
}, [setState]);
const bindBatteryEvents = React.useCallback(
(bat: BatteryManager) => {
on(bat, BatteryManagerEvents.chargingChange, onChange);
on(bat, BatteryManagerEvents.chargingTimeChange, onChange);
on(bat, BatteryManagerEvents.dischargingTimeChange, onChange);
on(bat, BatteryManagerEvents.levelChange, onChange);
},
[onChange]
);
const unbindBatteryEvents = React.useCallback(
(bat: BatteryManager) => {
off(bat, BatteryManagerEvents.chargingChange, onChange);
off(bat, BatteryManagerEvents.chargingTimeChange, onChange);
off(bat, BatteryManagerEvents.dischargingTimeChange, onChange);
off(bat, BatteryManagerEvents.levelChange, onChange);
},
[onChange]
);
React.useEffect(() => {
battery.current && bindBatteryEvents(battery.current);
return () => {
battery.current && unbindBatteryEvents(battery.current);
};
}, [onChange]);
React.useEffect(() => {
nav.getBattery!().then((bat: BatteryManager) => {
if (!isMounted) {
return;
}
battery.current = bat;
bindBatteryEvents(bat);
});
return () => {
isMounted = false;
battery.current && unbindBatteryEvents(battery.current);
};
}, []);
}
return state;
}