Building a React Hook for the Battery Status API
Hello and welcome to my first React post!
Because I love the Web and I’m also a huge fan of the React framework, I’m very happy to share this article with you.
But enough of the intro — let’s get into the topic!
I’m writing this post because I created a custom hook called useBatteryStatus.
I’ll explain how to build the hook from scratch and later show you how to install and use it yourself.
Let’s start from the beginning.
What is the Battery Status API?
If you’re not familiar with what the API does or what can be achieved with the Battery Status API, I already have a dedicated post about it:
👉 Hands-on Battery Status API.
You can read that first and then continue here.
The Hook
When working with React, I love to keep things small and easy to reuse later on.
That’s also the reason why I love hooks — and why they became so popular.
So I decided to implement this Web API as a React hook, and I named it useBatteryStatus.
Get the BatteryManager
As we learned from the other post, there’s a BatteryManager
object that’s returned by the Promise request for the API.
This object is created once per browser window.
In React, each hook instance is isolated, which is a bit tricky in this case.
If we call the Promise inside every hook instance, only the first hook would work — all other instances would lose their event listeners and stop updating.
Let’s take a look at the code:
let batteryManager: BatteryManager | null = null;
async function getBatteryManager() {
if (!navigator.getBattery) {
console.warn("Battery Status API is not supported on this browser.");
return null;
}
if (!batteryManager) {
batteryManager = await navigator.getBattery();
}
return batteryManager;
}
We call this function instead of always calling navigator.getBattery().
Here’s what happens:
- We check whether the browser supports the API. If not, we log a warning and return null.
- If no batteryManager has been created yet (first hook instance), we request it and wait for the Promise to resolve.
- If there’s already a batteryManager (when multiple hooks are used on your page), we simply return the existing instance.
That ensures there’s only one call to getBattery() in total.
Initialize Hook Data
First, let’s define the data structure we want to return.
Here’s the interface:
interface BatteryStatus {
charging: boolean;
level: number;
chargingTime: number;
dischargingTime: number;
}
Nothing surprising here — these properties all exist on the BatteryManager object as well.
Here’s how we initialize the state:
const [batteryStatus, setBatteryStatus] = useState<BatteryStatus>({
charging: false,
level: 0,
chargingTime: Infinity,
dischargingTime: Infinity
});
battery = await getBatteryManager();
if (!battery) return;
setBatteryStatus({
charging: battery.charging,
level: battery.level,
chargingTime: battery.chargingTime,
dischargingTime: battery.dischargingTime
});
As you can see, we’re using the helper function from before to get the BatteryManager object.
If the browser supports the Battery Status API, we update the state with the current battery data.
Add event listeners
const handleChange = () => {
setBatteryStatus({
charging: battery!.charging,
level: battery!.level,
chargingTime: battery!.chargingTime,
dischargingTime: battery!.dischargingTime
});
}
battery.addEventListener("chargingchange", handleChange);
battery.addEventListener("levelchange", handleChange);
battery.addEventListener("chargingtimechange", handleChange);
battery.addEventListener("dischargingtimechange", handleChange);
In this case, we’re simply listening for all supported Battery Status events.
Whenever something changes, we update our React state accordingly.
Of course, we should also remove the event listeners when the hook’s useEffect cleanup runs.
You can find the full code at the end of this post.
That’s really all the magic we need — pretty simple, right?
But it makes working with the Battery Status API in React much more convenient.
Use the Hook
Now that we’ve built the hook, let’s see how to install and use it:
npm install @nikadev/use-battery-status --save
# or
yarn add @nikadev/use-battery-status
Then inside your app or component:
import { useBatteryStatus } from "@nikadev/use-battery-status";
export function Component() {
const { level, charging, chargingTime, dischargingTime, isSupported } = useBatteryStatus();
if(!isSupported) {
return (
<p>Battery Status API is not supported in this browser.</p>
)
}
return (
<>
<h1>Battery Status API</h1>
<section>
<p>
<span>charging:</span>
<span>{charging ? 'Yes' : 'No'}</span>
</p>
<p>
<span>chargingTime:</span>
<span>{chargingTime}{chargingTime !== Infinity ? 's' : null}</span>
</p>
<p>
<span>dischargingTime:</span>
<span>{dischargingTime}{dischargingTime !== Infinity ? 's' : null}</span>
</p>
<p>
<span>level:</span>
<span>{level * 100}%</span>
</p>
</section>
</>
)
}
Example
You can explore an example in the repository itself:
👉 useBatteryStatus GitHub
Or check out the live demo:
👉 Vercel Example Page.
Conclusion
We’ve seen how the useBatteryStatus hook encapsulates the logic and data of the Battery Status API, making it easy to use in React.
If you’d like to work with the Battery Status API in your own React project, give my hook a try:
👉 useBatteryStatus.
I’d love to hear your thoughts or see what you build with it — let me know in the comments below!
Useful links
Top comments (0)