Skip to main content

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

currentPodcast
object | null
The podcast episode currently loaded in the player. Contains episode metadata like title, audio URL, image, etc.
isPlaying
boolean
Indicates whether the audio is actively playing (true) or paused (false)
showPlayer
boolean
Controls the visibility of the persistent player component

Actions

setCurrentPodcast

Sets the current podcast episode to play.
action.payload
object
required
The podcast episode object to load
title
string
required
Episode title
audio
string
required
Audio file URL
image
string
Episode thumbnail image URL
pubDate
string
Publication date
dispatch(setCurrentPodcast(podcastObject));
Usage Example:
src/App.js
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.
action.payload
boolean
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:
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.
action.payload
boolean
required
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.
dispatch(closePlayer());
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:
src/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:
src/App.js
const { currentPodcast, showPlayer } = useSelector(
  (state) => state.player
);

Player Workflow

Typical flow when playing a podcast:
1

Load Podcast

dispatch(setCurrentPodcast(podcast));
Sets the podcast to be played
2

Start Playback

dispatch(togglePlay(true));
Starts audio playback
3

Show Player

dispatch(setShowPlayer(true));
Displays the persistent player UI
4

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]);
  
  // ...
};

Build docs developers (and LLMs) love