Skip to content

Commit

Permalink
Improve performance by ditching 'isPlaying'
Browse files Browse the repository at this point in the history
  • Loading branch information
joshwcomeau committed Jun 4, 2021
1 parent 922711c commit 6be141d
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 43 deletions.
14 changes: 6 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,17 +253,15 @@ const [play, exposedData] = useSound('/meow.mp3');
// ^ What we're talking about
```

| Name | Value |
| --------- | -------------------------------- |
| stop | function ((id?: string) => void) |
| pause | function ((id?: string) => void) |
| isPlaying | boolean |
| duration | number (or null) |
| sound | Howl (or null) |
| Name | Value |
| -------- | -------------------------------- |
| stop | function ((id?: string) => void) |
| pause | function ((id?: string) => void) |
| duration | number (or null) |
| sound | Howl (or null) |

- `stop` is a function you can use to pre-emptively halt the sound.
- `pause` is like `stop`, except it can be resumed from the same point. Unless you know you'll want to resume, you should use `stop`; `pause` hogs resources, since it expects to be resumed at some point.
- `isPlaying` lets you know whether this sound is currently playing or not. When the sound reaches the end, or it's interrupted with `stop` or `paused`, this value will flip back to `false`. You can use this to show some UI only while the sound is playing.
- `duration` is the length of the sample, in milliseconds. It will be `null` until the sample has been loaded. Note that for sprites, it's the length of the entire file.
- `sound` is an escape hatch. It grants you access to the underlying `Howl` instance. See the [Howler documentation](https://github.com/goldfire/howler.js) to learn more about how to use it. Note that this will be `null` for the first few moments after the component mounts.

Expand Down
31 changes: 5 additions & 26 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { HookOptions, PlayOptions, PlayFunction, ReturnedValue } from './types';
export default function useSound<T = any>(
src: string | string[],
{
id,
volume = 1,
playbackRate = 1,
soundEnabled = true,
Expand All @@ -18,7 +19,6 @@ export default function useSound<T = any>(
const HowlConstructor = React.useRef<HowlStatic | null>(null);
const isMounted = React.useRef(false);

const [isPlaying, setIsPlaying] = React.useState(false);
const [duration, setDuration] = React.useState<number | null>(null);

const [sound, setSound] = React.useState<Howl | null>(null);
Expand All @@ -33,6 +33,9 @@ export default function useSound<T = any>(
// @ts-ignore
setDuration(this.duration() * 1000);
}

// @ts-ignore
setSound(this);
};

// We want to lazy-load Howler, since sounds can't play on load anyway.
Expand All @@ -42,15 +45,13 @@ export default function useSound<T = any>(
HowlConstructor.current = mod.Howl;
isMounted.current = true;

const sound = new HowlConstructor.current({
new HowlConstructor.current({
src: Array.isArray(src) ? src : [src],
volume,
rate: playbackRate,
onload: handleLoad,
...delegated,
});

setSound(sound);
}
});

Expand Down Expand Up @@ -115,19 +116,6 @@ export default function useSound<T = any>(
}

sound.play(options.id);

if (isMounted.current) {
sound.once('end', () => {
// If sound is not looping
if (!sound.playing()) {
setIsPlaying(false);
}
});
}

if (isMounted.current) {
setIsPlaying(true);
}
},
[sound, soundEnabled, interrupt]
);
Expand All @@ -138,10 +126,6 @@ export default function useSound<T = any>(
return;
}
sound.stop(id);

if (isMounted.current) {
setIsPlaying(false);
}
},
[sound]
);
Expand All @@ -152,10 +136,6 @@ export default function useSound<T = any>(
return;
}
sound.pause(id);

if (isMounted.current) {
setIsPlaying(false);
}
},
[sound]
);
Expand All @@ -166,7 +146,6 @@ export default function useSound<T = any>(
sound,
stop,
pause,
isPlaying,
duration,
},
];
Expand Down
1 change: 0 additions & 1 deletion src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export interface ExposedData {
sound: Howl | null;
stop: (id?: string) => void;
pause: (id?: string) => void;
isPlaying: boolean;
duration: number | null;
}
export declare type ReturnedValue = [PlayFunction, ExposedData];
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export type SpriteMap = {
};

export type HookOptions<T = any> = T & {
id?: string;
volume?: number;
playbackRate?: number;
interrupt?: boolean;
Expand All @@ -23,7 +24,6 @@ export interface ExposedData {
sound: Howl | null;
stop: (id?: string) => void;
pause: (id?: string) => void;
isPlaying: boolean;
duration: number | null;
}

Expand Down
6 changes: 3 additions & 3 deletions stories/demos/Checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ const BORDER_WIDTH = 2;
function CheckboxDemo() {
const [isChecked, setIsChecked] = React.useState(false);

const [playActive] = useSound(popDown, { volume: 0.25 });
const [playOn] = useSound(popUpOn, { volume: 0.25 });
const [playOff] = useSound(popUpOff, { volume: 0.25 });
const [playActive] = useSound(popDown, { id: 'active', volume: 0.25 });
const [playOn] = useSound(popUpOn, { id: 'on', volume: 0.25 });
const [playOff] = useSound(popUpOff, { id: 'off', volume: 0.25 });

return (
<Checkbox
Expand Down
24 changes: 24 additions & 0 deletions stories/demos/ShowWhilePlaying.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import useSound from '@';

import dunDunDunSfx from '../sounds/dun-dun-dun.mp3';
import Button from '../helpers/Button';

const ShowWhilePlaying = () => {
const [isPlaying, setIsPlaying] = React.useState(false);

const [playBoop] = useSound(dunDunDunSfx, {
onplay: () => setIsPlaying(true),
onend: () => setIsPlaying(false),
});

return (
<>
<h2>Is playing: {isPlaying.toString()}</h2>
<br />
<Button onClick={playBoop}>Play sound</Button>
</>
);
};

export default ShowWhilePlaying;
15 changes: 11 additions & 4 deletions stories/use-sound.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import HoverDemo from './demos/Hover';
import RisingDemo from './demos/Rising';
import DrumMachineDemo from './demos/DrumMachine';
import MultipleSourcesDemo from './demos/SimpleMultipleSources';
import ShowWhilePlayingDemo from './demos/ShowWhilePlaying';

import 'focus-visible';

Expand All @@ -28,8 +29,6 @@ Simple.story = {
export const Checkbox = () => (
<div style={{ display: 'flex', width: 160, justifyContent: 'space-between' }}>
<CheckboxDemo />
<CheckboxDemo />
<CheckboxDemo />
</div>
);

Expand Down Expand Up @@ -64,12 +63,20 @@ DrumMachine.story = {
export const MultipleSources = () => {
const options = {
'wav/mp3': 'wav_mp3',
'mp3/wav': 'mp3_wav'
'mp3/wav': 'mp3_wav',
};
const value = radios('Source', options, 'wav_mp3', 'group1');
return <MultipleSourcesDemo order={value}/>;
return <MultipleSourcesDemo order={value} />;
};

MultipleSources.story = {
name: 'Multiple sources support',
};

export const ShowWhilePlaying = () => {
return <ShowWhilePlayingDemo />;
};

ShowWhilePlaying.story = {
name: 'With Howler events (show while playing)',
};

0 comments on commit 6be141d

Please sign in to comment.