javascript - React HTMLAudioElement 不会暂停
问题描述
我在 React 中有一个组件,它接受一个 URL 并显示一个让用户停止/暂停音频文件的按钮。该组件在页面上多次使用。
该按钮可以很好地启动音频,但它audio.pause()
不会停止流。
我正在使用 Next.js,但是我正在像这样导入组件:
const HistoryRow = dynamic(() => import('../components/HistoryRow'), { ssr: false });
有人知道为什么这不会停止流吗?我可以确认它正在输入 if 语句就好了。
import { useState } from 'react';
import m from 'moment';
import path from 'path';
function HistoryRow({ data }) {
const [playing, setPlaying] = useState(false);
const audio = new Audio(`${window.location.origin}/audio-responses/${data.timestamp}.wav`);
audio.onended = function () {
setPlaying(false);
};
audio.onpause = function () {
setPlaying(false);
};
audio.onplay = function () {
setPlaying(true);
};
audio.onerror = function () {
setPlaying(false);
};
function execute() {
if (playing) {
audio.pause();
} else {
audio.play();
}
}
const stopSvg = (
<svg> /** svg data ** / </svg>
);
const playSvg = (
<svg> /** svg data ** / </svg>
);
return (
<div className="text-base flex leading-5 font-medium text-blue-600 truncate" onClick={() => execute()}>
{data.type === 'command' ? (playing ? stopSvg : playSvg) : null}
</div>
);
}
export default HistoryRow;
解决方案
每当您声明更改时,都会重新渲染整个组件。这意味着组件中的每个变量都被重写。因此,您创建一个Audio
实例,然后开始播放,组件重新渲染并Audio
创建一个新实例,因此您失去了与前一个实例的绑定。
您可以使用该useRef
钩子创建对Audio
在组件的整个生命周期内持续存在的实例的引用。所以它永远不会改变,除非你明确告诉它。current
您可以使用返回useRef
值的属性访问实例。
由于 React 是状态驱动的,我建议使用useEffect
钩子来监听状态的变化,playing
并根据状态的值开始或停止播放playing
,这是你目前拥有的另一种方式。
function HistoryRow({ data }) {
const [playing, setPlaying] = useState(false);
const [hasError, setHasError] = useState(false);
const audio = useRef(new Audio(`${window.location.origin}/audio-responses/${data.timestamp}.wav`));
audio.current.onended = function () {
setPlaying(false);
};
audio.current.onplay = function () {
setHasError(false);
};
const handleClick = () => {
setPlaying(playing => !playing);
});
useEffect(() => {
if (playing) {
audio.current.play().then(() => {
// Audio is playing.
}).catch(error => {
setHasError(true);
});
} else if (!hasError) {
audio.current.pause();
}
}, [playing, hasError]);
const stopSvg = (
<svg> /* svg data */ </svg>
);
const playSvg = (
<svg> /* svg data */ </svg>
);
return (
<div
className="text-base flex leading-5 font-medium text-blue-600 truncate"
onClick={handleClick}>
{data.type === 'command' ? (playing ? stopSvg : playSvg) : null}
</div>
);
}
推荐阅读
- python - 预测游戏结果的下一个值
- python - 如何在请求中流式传输文件?
- javascript - 是否可以将多个对象数组保存为一个?
- reactjs - react组件渲染方法被无缘无故调用了两次
- ios - 使用 Accuracy 宏措辞或错误来误导 XCTAssertEqual?
- r - 为什么 stringr 和 forcats 中替换的命名约定不同?
- codenameone - 与 EDT 不同的线程的 ErrorHandler
- java - Spring MVC - 使用 Tomcat 获取恒定的 404 错误
- javascript - Javascript在字符串中添加变量和数字
- kicad - 如何解决 KiCad 中的足迹丢失错误