So I've created a hook that will read my browser's devices and their permissions:
/**
* @param {PermissionName} name The permission name will be checked. Will be one of
* "geolocation" | "notifications" | "persistent-storage" | "push" | "screen-wake-lock" | "xr-spatial-tracking";
* @returns {PermissionStatus} permission
*/
export const useCurrentDevicePermissions = (name: PermissionName): PermissionStatus | undefined => {
const [permission, setPermission] = useState<PermissionStatus>();
useEffect(() => {
navigator.permissions.query({ name }).then(setPermission);
}, [permission]);
return permission;
};
and it's being used like this:
const cameraPermissionState = useCurrentDevicePermissions('camera' as PermissionName);
Which works, and will update when the permissions update. However, when I log this out, I get a bunch of logs (presumably because it's constantly checking for new permissions):
I've tried useMemo
but I'm not entirely sure if that's the solution. How do I render this hook once soo it'll look something like
{
name: 'video_capture', state: 'prompt', onchange: null
} // log once
and once granted
or denied
{
name: 'video_capture', state: 'granted', onchange: null
} // log once
CodePudding user response:
You've created a loop using permission
as a dependency if the useEffect
block. When the permission
changes, useEffect
triggers, and sets the permission
state with a new object causing useEffect
to trigger...
Since the useEffect
uses the name
parameter in the callback, and you want it to trigger when the name changes use it as the dependency.
If you want to track permission status changes, register to the change
event of the PermissionStatus
object.
Example:
const { useState, useEffect } = React;
const useCurrentDevicePermissions = (name) => {
const [permission, setPermission] = useState(null);
useEffect(() => {
navigator.permissions.query({ name }).then(setPermission);
}, [name]);
useEffect(() => {
if(!permission) return;
const changeHandler = e => setPermission(e.target);
permission.addEventListener('change', changeHandler);
return () => {
permission.removeEventListener('change', changeHandler);
};
}, [permission]);
return permission;
};
const Demo = () => {
const permission = useCurrentDevicePermissions('notifications');
if(!permission) return null;
return (
<div>{permission.name} is {permission.state}</div>
);
};
ReactDOM
.createRoot(root)
.render(<Demo />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="root"></div>