Don't use useMemo()
for AudioContext/Audio/Cache
`useMemo()` doesn't guarantee that the value won't be recomputed during re-renders. Unfortunately, every time `AudioContext` is instantiated - there is an audible click. This click happens during the change between conversations and is very annoying. Move both `AudioContext` instance to the GlobalAudioContext's top-level declarations, and `Audio`/`WaveformCache` to `useRef()`s.
This commit is contained in:
parent
0bd3c78187
commit
8c9e556a22
2 changed files with 45 additions and 37 deletions
|
@ -21,6 +21,8 @@ export type GlobalAudioProps = {
|
||||||
children?: React.ReactNode | React.ReactChildren;
|
children?: React.ReactNode | React.ReactChildren;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const audioContext = new AudioContext();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A global context that holds Audio, AudioContext, LRU instances that are used
|
* A global context that holds Audio, AudioContext, LRU instances that are used
|
||||||
* inside the conversation by ts/components/conversation/MessageAudio.tsx
|
* inside the conversation by ts/components/conversation/MessageAudio.tsx
|
||||||
|
@ -29,45 +31,33 @@ export const GlobalAudioProvider: React.FC<GlobalAudioProps> = ({
|
||||||
conversationId,
|
conversationId,
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const audio = React.useMemo(() => {
|
const audio = React.useRef<HTMLAudioElement | null>(null);
|
||||||
window.log.info(
|
const waveformCache = React.useRef<WaveformCache | null>(null);
|
||||||
'GlobalAudioProvider: re-generating audio for',
|
|
||||||
conversationId
|
|
||||||
);
|
|
||||||
return new Audio();
|
|
||||||
}, [conversationId]);
|
|
||||||
|
|
||||||
// NOTE: the number of active audio contexts is limited per tab/window
|
// NOTE: We don't want to construct these values on every re-render hence
|
||||||
// See: https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/AudioContext#google_chrome
|
// the constructor calls have to be guarded by `if`s.
|
||||||
const audioContext = React.useMemo(() => {
|
if (!audio.current) {
|
||||||
window.log.info('Instantiating new audio context');
|
audio.current = new Audio();
|
||||||
return new AudioContext();
|
}
|
||||||
}, []);
|
if (!waveformCache.current) {
|
||||||
|
waveformCache.current = new LRU({
|
||||||
const waveformCache: WaveformCache = React.useMemo(() => {
|
|
||||||
return new LRU({
|
|
||||||
max: MAX_WAVEFORM_COUNT,
|
max: MAX_WAVEFORM_COUNT,
|
||||||
});
|
});
|
||||||
}, []);
|
}
|
||||||
|
|
||||||
// When moving between conversations - stop audio
|
// When moving between conversations - stop audio
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
audio.pause();
|
if (audio.current) {
|
||||||
|
audio.current.pause();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [audio, conversationId]);
|
}, [conversationId]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
window.log.info('Closing old audio context');
|
|
||||||
audioContext.close();
|
|
||||||
};
|
|
||||||
}, [audioContext]);
|
|
||||||
|
|
||||||
const value = {
|
const value = {
|
||||||
audio,
|
audio: audio.current,
|
||||||
audioContext,
|
audioContext,
|
||||||
waveformCache,
|
waveformCache: waveformCache.current,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -11903,6 +11903,14 @@
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2018-09-19T18:06:35.446Z"
|
"updated": "2018-09-19T18:06:35.446Z"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"rule": "jQuery-wrap(",
|
||||||
|
"path": "node_modules/pino/browser.js",
|
||||||
|
"line": " wrap(opts, logger, level)",
|
||||||
|
"lineNumber": 181,
|
||||||
|
"reasonCategory": "falseMatch",
|
||||||
|
"updated": "2021-03-09T20:56:35.403Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"rule": "jQuery-$(",
|
"rule": "jQuery-$(",
|
||||||
"path": "node_modules/popper.js/dist/esm/popper.min.js",
|
"path": "node_modules/popper.js/dist/esm/popper.min.js",
|
||||||
|
@ -14696,6 +14704,24 @@
|
||||||
"updated": "2020-11-11T21:56:04.179Z",
|
"updated": "2020-11-11T21:56:04.179Z",
|
||||||
"reasonDetail": "Needed to render the remote video element."
|
"reasonDetail": "Needed to render the remote video element."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"rule": "React-useRef",
|
||||||
|
"path": "ts/components/GlobalAudioContext.js",
|
||||||
|
"line": " const audio = React.useRef(null);",
|
||||||
|
"lineNumber": 38,
|
||||||
|
"reasonCategory": "usageTrusted",
|
||||||
|
"updated": "2021-03-11T17:20:05.355Z",
|
||||||
|
"reasonDetail": "Need this to avoid re-creating Audio on every re-render"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rule": "React-useRef",
|
||||||
|
"path": "ts/components/GlobalAudioContext.js",
|
||||||
|
"line": " const waveformCache = React.useRef(null);",
|
||||||
|
"lineNumber": 39,
|
||||||
|
"reasonCategory": "usageTrusted",
|
||||||
|
"updated": "2021-03-11T17:20:05.355Z",
|
||||||
|
"reasonDetail": "Need this to avoid re-creating WaveformCache on every re-render"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"rule": "React-useRef",
|
"rule": "React-useRef",
|
||||||
"path": "ts/components/GroupCallOverflowArea.js",
|
"path": "ts/components/GroupCallOverflowArea.js",
|
||||||
|
@ -15444,13 +15470,5 @@
|
||||||
"reasonCategory": "usageTrusted",
|
"reasonCategory": "usageTrusted",
|
||||||
"updated": "2021-01-08T15:46:32.143Z",
|
"updated": "2021-01-08T15:46:32.143Z",
|
||||||
"reasonDetail": "Doesn't manipulate the DOM. This is just a function."
|
"reasonDetail": "Doesn't manipulate the DOM. This is just a function."
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-wrap(",
|
|
||||||
"path": "node_modules/pino/browser.js",
|
|
||||||
"line": " wrap(opts, logger, level)",
|
|
||||||
"lineNumber": 181,
|
|
||||||
"reasonCategory": "falseMatch",
|
|
||||||
"updated": "2021-03-09T20:56:35.403Z"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
Loading…
Add table
Reference in a new issue