假设我正在制作一个音乐网站。我创建了一个 MusicPlayer 组件:
function MusicPlayer() {
const [playingTrackId, setPlayingTrackId] = useState<number | null>(null)
useEffect(
() => {
console.log(`Stopping all audio of this player`) // Doesn't affect other players
if (playingTrackId !== null) {
console.log(`Playing track ${playingTrackId}`)
}
},
[playingTrackId]
)
const playstop = (trackId: number) => {
if (trackId === playingTrackId) {
setPlayingTrackId(null)
} else {
setPlayingTrackId(trackId)
}
}
return (
<div>
<button onClick={() => playstop(1)}>Play | Stop track 1</button>
<button onClick={() => playstop(2)}>Play | Stop track 2</button>
</div>
)
}
一切都好!但假设出于某种原因,我想在一个页面上放置两个独立的音乐播放器:
function Page() {
return (
<div>
<MusicPlayer/>
<MusicPlayer/>
</div>
)
}
现在我想实现另一个功能:我希望音乐播放器在每次另一个播放器开始播放时停止播放音乐,这样两个曲目就不会同时播放。 实施它的最佳方法是什么?
要实现这样的目标,您需要使用 React Context
您需要首先制作一个音乐处理程序组件,该组件具有全局播放曲目 id 状态,并且还包括用于播放和停止音频的大部分逻辑。
MusicHandlerContext.tsx
import React from "react";
export const MusicHandlerContext = React.createContext<{
playingTrackId: number | null;
playTrack: (trackId: number) => void;
stopPlaying: () => void;
}>({
playingTrackId: null,
playTrack: (_: number) => {},
stopPlaying: () => {},
});
function MusicHandler({ children }: React.PropsWithChildren) {
const [playingTrackId, setPlayingTrackId] = React.useState<number | null>(
null
);
return (
<MusicHandlerContext.Provider
value={{
playingTrackId,
playTrack: (trackId) => {
console.log(`Playing track ${trackId}`);
setPlayingTrackId(trackId);
},
stopPlaying: () => {
console.log(`Stopping all audio`);
setPlayingTrackId(null);
},
}}
>
{children}
</MusicHandlerContext.Provider>
);
}
export default MusicHandler;
现在,这个
MusicHandler
组件的所有子组件都将能够访问我们使用 useContext 钩子创建的小“api”。
为了确保任何
MusicPlayer
都在 MusicHandler 中,我建议将其放入顶级文件中,对我来说就是 App.tsx
,如下所示:
应用程序.tsx
import MusicHandler from "./components/musicHandler";
import MusicPlayer from "./components/musicPlayer";
function App() {
return (
<MusicHandler>
<p>Music Player 1</p>
<MusicPlayer />
<div>
<p>Music Player 2</p>
<MusicPlayer />
</div>
</MusicHandler>
);
}
export default App;
如果
MusicPlayer
不在 MusicHandler
之内,则此设置将不起作用,并且要使其正常工作,在任何给定时间都应该只有一个 MusicHandler
现在,如果我们更改
MusicPlayer
组件以使用这个新的全局状态,我们现在可以轻松地停止从任何组件播放所有音乐,并且根据设计,确保不能同时播放两个曲目
音乐播放器.tsx
import React from "react";
import { MusicHandlerContext } from "./musicHandler";
function MusicPlayer() {
const { playingTrackId, playTrack, stopPlaying } =
React.useContext(MusicHandlerContext);
const playstop = (trackId: number) => {
if (trackId === playingTrackId) {
stopPlaying();
} else {
playTrack(trackId);
}
};
return (
<div>
<button onClick={() => playstop(1)}>Play | Stop track 1</button>
<button onClick={() => playstop(2)}>Play | Stop track 2</button>
</div>
);
}
export default MusicPlayer;
像这样编写系统也是比以前更好的主意,因为它将代码分开,几乎所有逻辑都在
MusicHandler
中,而大部分实际的ui都在MusicPlayer
中,如下所示它应该是。如果您愿意,您也可以轻松地将 playstop()
函数移至 MusicHandler
。
您仍然需要实际实现
playTrack()
和 stopPlaying()
函数,但之后它应该可以工作。
React Context 是 React 中的一个关键概念,因此我建议您阅读涵盖主题的 React 文档,但我希望这个示例展示了它在哪些方面非常有用。