Overview
The player slice manages the audio player state, including:
Currently playing podcast episode
Play/pause state
Persistent player visibility
Player lifecycle management
State Shape
{
currentPodcast : null , // Currently loaded podcast object
isPlaying : false , // Whether audio is currently playing
showPlayer : false // Whether persistent player is visible
}
State Properties
The podcast episode currently loaded in the player. Contains episode metadata like title, audio URL, image, etc.
Indicates whether the audio is actively playing (true) or paused (false)
Controls the visibility of the persistent player component
Actions
setCurrentPodcast
Sets the current podcast episode to play.
The podcast episode object to load Episode thumbnail image URL
dispatch ( setCurrentPodcast ( podcastObject ));
Usage Example:
const handlePlayPodcast = ( podcast ) => {
if ( currentPodcast && currentPodcast . title === podcast . title ) {
dispatch ( togglePlay ());
} else {
dispatch ( setCurrentPodcast ( podcast ));
dispatch ( togglePlay ( true ));
}
dispatch ( setShowPlayer ( true ));
};
togglePlay
Toggles the play/pause state or sets it explicitly.
Optional explicit value. If not provided, toggles current state.
// Toggle play/pause
dispatch ( togglePlay ());
// Set to playing
dispatch ( togglePlay ( true ));
// Set to paused
dispatch ( togglePlay ( false ));
Reducer Logic:
src/store/slices/playerSlice.js
togglePlay : ( state , action ) => {
state . isPlaying = action . payload !== undefined
? action . payload
: ! state . isPlaying ;
}
Usage Examples:
PersistentPlayer Component
PodcastDetail Component
const handlePlay = () => {
if ( completedEpisodes . includes ( currentPodcast . title )) {
dispatch ( removeFromCompleted ( currentPodcast . title ));
}
dispatch ( togglePlay ( true ));
};
// In AudioPlayer component
< AudioPlayer
onPlay = { handlePlay }
onPause = { () => dispatch ( togglePlay ( false )) }
/>
setShowPlayer
Controls the visibility of the persistent player.
true to show the player, false to hide it
// Show player
dispatch ( setShowPlayer ( true ));
// Hide player
dispatch ( setShowPlayer ( false ));
closePlayer
Stops playback and hides the player.
Reducer Logic:
src/store/slices/playerSlice.js
closePlayer : ( state ) => {
state . isPlaying = false ;
state . showPlayer = false ;
}
This action does NOT clear the currentPodcast. Use clearCurrentPodcast for that.
clearCurrentPodcast
Clears the currently loaded podcast.
dispatch ( clearCurrentPodcast ());
Reducer Logic:
src/store/slices/playerSlice.js
clearCurrentPodcast : ( state ) => {
state . currentPodcast = null ;
}
Async Thunks
closePersistentPlayerAsync
Gracefully closes the persistent player with a delay between closing and clearing.
dispatch ( closePersistentPlayerAsync ());
Implementation:
src/store/slices/playerSlice.js
export const closePersistentPlayerAsync = createAsyncThunk (
"player/closePersistentPlayerAsync" ,
( _ , { dispatch }) => {
dispatch ( playerSlice . actions . closePlayer ());
setTimeout (() => {
dispatch ( playerSlice . actions . clearCurrentPodcast ());
}, 800 );
}
);
The 800ms delay allows the exit animation to complete before clearing the podcast data.
Usage in App.js:
import { closePersistentPlayerAsync } from './store/slices/playerSlice' ;
const App = () => {
// ...
return (
< AnimatePresence >
{ showPlayer && (
< PersistentPlayer
onClose = { () => dispatch ( closePersistentPlayerAsync ()) }
/>
) }
</ AnimatePresence >
);
};
Selectors
Access player state in components:
import { useSelector } from 'react-redux' ;
const MyComponent = () => {
const { currentPodcast , isPlaying , showPlayer } = useSelector (
( state ) => state . player
);
// Use the state...
};
Real Examples:
App.js
PodcastDetail.jsx
PersistentPlayer.jsx
const { currentPodcast , showPlayer } = useSelector (
( state ) => state . player
);
const { currentPodcast , isPlaying } = useSelector (
( state ) => state . player
);
const isPodcastPlaying =
isPlaying &&
currentPodcast &&
currentPodcast . title === podcast . title ;
const { currentPodcast , isPlaying } = useSelector (
( state ) => state . player
);
if ( ! currentPodcast ) return null ;
Player Workflow
Typical flow when playing a podcast:
Load Podcast
dispatch ( setCurrentPodcast ( podcast ));
Sets the podcast to be played
Start Playback
dispatch ( togglePlay ( true ));
Starts audio playback
Show Player
dispatch ( setShowPlayer ( true ));
Displays the persistent player UI
Close Player
dispatch ( closePersistentPlayerAsync ());
Stops playback, hides UI, and clears after animation
Complete Action Reference
import {
setCurrentPodcast , // Set current podcast
togglePlay , // Toggle or set play state
setShowPlayer , // Show/hide player
closePlayer , // Stop and hide player
clearCurrentPodcast , // Clear current podcast
closePersistentPlayerAsync // Async close with delay
} from './store/slices/playerSlice' ;
Integration with PersistentPlayer
The PersistentPlayer component responds to player state:
src/components/PersistentPlayer/PersistentPlayer.jsx
const PersistentPlayer = ({ onClose }) => {
const { currentPodcast , isPlaying } = useSelector (
( state ) => state . player
);
useEffect (() => {
if ( audioRef . current && currentPodcast ) {
const audio = audioRef . current . audio . current ;
if ( isPlaying ) {
audio . play ();
} else {
audio . pause ();
}
}
}, [ isPlaying , currentPodcast ]);
// ...
};