如何通过React Context实现音乐播放器组件间currentSong数据传递?
用React Context实现歌曲状态共享的完整方案
步骤1:创建PlayerContext
首先需要创建一个Context对象,作为状态传递的载体:
const PlayerContext = React.createContext();
步骤2:完善PlayerProvider组件
将歌曲选中状态的管理从Songs组件移到PlayerProvider中,让它成为所有子组件的共享状态,同时提供修改状态的方法:
const PlayerProvider = ({ children }) => { const [currentSong, setCurrSongSel] = React.useState(''); // 把状态和修改方法存入context值 const contextValue = { currentSong, setCurrSongSel }; return ( <PlayerContext.Provider value={contextValue}> {children} </PlayerContext.Provider> ); };
步骤3:实现usePlayerContext自定义Hook
封装useContext调用逻辑,简化组件获取Context的过程,同时添加校验确保组件在Provider内部使用:
const usePlayerContext = () => { const context = React.useContext(PlayerContext); if (!context) { throw new Error('usePlayerContext must be used within a PlayerProvider'); } return context; };
步骤4:修改Songs组件
移除自身的useState,通过自定义Hook获取Context中的状态和修改方法,点击歌曲时更新选中状态:
const Songs = () => { const { currentSong, setCurrSongSel } = usePlayerContext(); return ( <PlayList> {songList.map(({ title, author, id }) => ( <Song key={id} onClick={()=> setCurrSongSel(id)}> <SongTitle data-testid={id} active={currentSong === id}> {title} </SongTitle> <p>{author}</p> </Song> ))} </PlayList> ); };
步骤5:修改ControlBar组件
通过自定义Hook获取当前选中的歌曲ID,匹配对应的歌曲信息并显示:
const ControlBar = () => { const { currentSong } = usePlayerContext(); // 根据选中ID匹配歌曲,默认显示提示文本 const selectedSong = songList.find(song => song.id === currentSong) || {}; const title = selectedSong.title ? `${selectedSong.title} - ${selectedSong.author}` : '未选择歌曲'; return ( <BottomBar> <BarSongTitle data-testid="barTitle">{title}</BarSongTitle> <div> <Button data-testid="previousButton">Previous</Button> <Button data-testid="nextButton">Next</Button> <Button data-testid="currentModeButton">{buttonLabels[0]}</Button> </div> </BottomBar> ); };
完整修改后的代码
import React from 'react'; import { BarSongTitle, BottomBar, Button, PlayList, Song, SongTitle, } from './styles.js'; import { songList } from './constants.js'; const buttonLabels = ['Not replaying', 'Replaying all', 'Replaying one']; // 创建PlayerContext const PlayerContext = React.createContext(); // 完善PlayerProvider,管理共享状态 const PlayerProvider = ({ children }) => { const [currentSong, setCurrSongSel] = React.useState(''); const contextValue = { currentSong, setCurrSongSel }; return ( <PlayerContext.Provider value={contextValue}> {children} </PlayerContext.Provider> ); }; // 自定义Hook简化Context访问 const usePlayerContext = () => { const context = React.useContext(PlayerContext); if (!context) { throw new Error('usePlayerContext must be used within a PlayerProvider'); } return context; }; // 修改ControlBar获取共享状态 const ControlBar = () => { const { currentSong } = usePlayerContext(); const selectedSong = songList.find(song => song.id === currentSong) || {}; const title = selectedSong.title ? `${selectedSong.title} - ${selectedSong.author}` : '未选择歌曲'; return ( <BottomBar> <BarSongTitle data-testid="barTitle">{title}</BarSongTitle> <div> <Button data-testid="previousButton">Previous</Button> <Button data-testid="nextButton">Next</Button> <Button data-testid="currentModeButton">{buttonLabels[0]}</Button> </div> </BottomBar> ); }; // 修改Songs使用Context的状态和方法 const Songs = () => { const { currentSong, setCurrSongSel } = usePlayerContext(); return ( <PlayList> {songList.map(({ title, author, id }) => ( <Song key={id} onClick={()=> setCurrSongSel(id)}> <SongTitle data-testid={id} active={currentSong === id}> {title} </SongTitle> <p>{author}</p> </Song> ))} </PlayList> ); }; export { PlayerProvider, Songs, ControlBar };
核心逻辑说明
- 状态提升:将原本
Songs组件内的选中状态移到PlayerProvider中,让它成为所有子组件的共享状态。 - Context传递:通过
PlayerContext.Provider把状态和修改方法传递给所有嵌套子组件。 - 自定义Hook:封装Context获取逻辑,同时添加错误校验,避免组件在Provider外部使用时出现异常。
内容的提问来源于stack exchange,提问作者UI_Dev
相关产品推荐
相关产品推荐

