哪个 React 组件应该包含某种状态?

问题描述 投票:0回答:1

假设我正在制作一个音乐网站。我创建了一个 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>
    )
}

现在我想实现另一个功能:我希望音乐播放器在每次另一个播放器开始播放时停止播放音乐,这样两个曲目就不会同时播放。 实施它的最佳方法是什么?

javascript reactjs typescript react-hooks react-props
1个回答
0
投票

要实现这样的目标,您需要使用 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 文档,但我希望这个示例展示了它在哪些方面非常有用。

© www.soinside.com 2019 - 2024. All rights reserved.