fix: Reworked useBattery hook

This commit is contained in:
Vadim Dalecky 2019-08-20 23:25:05 +02:00 committed by GitHub
commit 106906013f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 134 additions and 40 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,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 (
<div>
<strong>Battery sensor</strong>: <span>not supported</span>
</div>
);
}
if (!batteryState.fetched) {
return (
<div>
<strong>Battery sensor</strong>: <span>supported</span> <br />
<strong>Battery state</strong>: <span>fetching</span>
</div>
);
}
return (
<pre>
{JSON.stringify(state, null, 2)}
</pre>
<div>
<strong>Battery sensor</strong>:&nbsp;&nbsp; <span>supported</span> <br />
<strong>Battery state</strong>: <span>fetched</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`_ - 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.

View File

@ -4,9 +4,36 @@ 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>: <span>not supported</span>
</div>
);
}
if (!batteryState.fetched) {
return (
<div>
<strong>Battery sensor</strong>: <span>supported</span> <br />
<strong>Battery state</strong>: <span>fetching</span>
</div>
);
}
return (
<div>
<strong>Battery sensor</strong>:&nbsp;&nbsp; <span>supported</span> <br />
<strong>Battery state</strong>: <span>fetched</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,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<BatteryState>, 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<BatteryManager>;
}
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<UseBatteryState>({ 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;