Skip to main content

Overview

The filter slice manages the podcast list filtering and pagination state. It’s a simple slice that controls:
  • Which category filter is active (all, favorites, completed, etc.)
  • Current page number
  • Items per page setting

State Shape

{
  currentFilter: "todos",   // Active filter: "todos" | "favoritos" | "empezados" | "completados" | "escuchar-mas-tarde"
  currentPage: 1,           // Current page number (1-indexed)
  songsPerPage: 12          // Number of episodes per page
}

State Properties

currentFilter
string
default:"todos"
The active filter category. Possible values:
  • "todos" - All episodes
  • "favoritos" - Favorite episodes
  • "empezados" - Started episodes
  • "completados" - Completed episodes
  • "escuchar-mas-tarde" - Listen later episodes
currentPage
number
default:1
The current page number in the paginated episode list. Pages start at 1.
songsPerPage
number
default:12
Number of episodes to display per page. This is currently a constant value.

Actions

setFilter

Sets the active filter and resets pagination to page 1.
action.payload
string
required
The filter to activate. Must be one of:
  • "todos"
  • "favoritos"
  • "empezados"
  • "completados"
  • "escuchar-mas-tarde"
dispatch(setFilter("favoritos"));
Reducer Logic:
src/store/slices/filterSlice.js
setFilter: (state, action) => {
  state.currentFilter = action.payload;
  state.currentPage = 1;  // Reset to first page
}
Setting a new filter automatically resets the page to 1, ensuring users start at the beginning of the filtered list.
Usage Example:
src/App.js
const handleSearchChange = (e) => {
  // Reset filter to "todos" when searching
  dispatch(setFilter("todos"));
  dispatch(setSearchTerm(e.target.value));
};

setCurrentPage

Changes the current page number.
action.payload
number
required
The page number to navigate to (1-indexed)
dispatch(setCurrentPage(3));
Reducer Logic:
src/store/slices/filterSlice.js
setCurrentPage: (state, action) => {
  state.currentPage = action.payload;
}

Selectors

Access filter state in components:
import { useSelector } from 'react-redux';

const MyComponent = () => {
  const { currentFilter, currentPage, songsPerPage } = useSelector(
    (state) => state.filter
  );
  
  // Use the state...
};

Filter Options

The available filter categories map to user collections:

todos

Shows all podcast episodes

favoritos

Shows episodes in favoriteEpisodes array

empezados

Shows episodes with saved playback times

completados

Shows episodes in completedEpisodes array

escuchar-mas-tarde

Shows episodes in listenLaterEpisodes array

Pagination Logic

Components use the filter state to calculate which episodes to display:
const { currentPage, songsPerPage } = useSelector(
  (state) => state.filter
);

// Calculate pagination indices
const indexOfLastSong = currentPage * songsPerPage;
const indexOfFirstSong = indexOfLastSong - songsPerPage;

// Get current page of episodes
const currentSongs = filteredEpisodes.slice(
  indexOfFirstSong, 
  indexOfLastSong
);

// Calculate total pages
const totalPages = Math.ceil(filteredEpisodes.length / songsPerPage);

Filter Integration Example

Typical usage pattern in a podcast list component:
import { useSelector, useDispatch } from 'react-redux';
import { setFilter, setCurrentPage } from './store/slices/filterSlice';

const PodcastList = () => {
  const dispatch = useDispatch();
  
  // Get all relevant state
  const { songs, favoriteEpisodes, completedEpisodes, searchTerm } = 
    useSelector((state) => state.podcast);
  const { playbackTimes } = useSelector((state) => state.audioTime);
  const { currentFilter, currentPage, songsPerPage } = 
    useSelector((state) => state.filter);
  
  // Filter episodes based on currentFilter
  const getFilteredSongs = () => {
    let filtered = songs;
    
    switch (currentFilter) {
      case "favoritos":
        filtered = songs.filter(song => 
          favoriteEpisodes.includes(song.title)
        );
        break;
      case "empezados":
        filtered = songs.filter(song => 
          playbackTimes[song.title] > 0
        );
        break;
      case "completados":
        filtered = songs.filter(song => 
          completedEpisodes.includes(song.title)
        );
        break;
      // ... other cases
    }
    
    // Apply search filter
    if (searchTerm) {
      filtered = filtered.filter(song =>
        song.title.toLowerCase().includes(searchTerm.toLowerCase())
      );
    }
    
    return filtered;
  };
  
  const filteredSongs = getFilteredSongs();
  
  // Pagination
  const indexOfLastSong = currentPage * songsPerPage;
  const indexOfFirstSong = indexOfLastSong - songsPerPage;
  const currentSongs = filteredSongs.slice(
    indexOfFirstSong, 
    indexOfLastSong
  );
  
  return (
    <div>
      {/* Filter buttons */}
      <button onClick={() => dispatch(setFilter("todos"))}
        Todos
      </button>
      <button onClick={() => dispatch(setFilter("favoritos"))}
        Favoritos
      </button>
      
      {/* Episode list */}
      {currentSongs.map(song => (
        <Episode key={song.title} episode={song} />
      ))}
      
      {/* Pagination */}
      <Pagination
        currentPage={currentPage}
        totalPages={Math.ceil(filteredSongs.length / songsPerPage)}
        onPageChange={(page) => dispatch(setCurrentPage(page))}
      />
    </div>
  );
};

Filter State Interactions

1

User Selects Filter

dispatch(setFilter("favoritos"));
Filter changes and page resets to 1
2

Episodes Are Filtered

Component filters the songs array based on currentFilter
3

Pagination Applied

Filtered episodes are sliced based on currentPage and songsPerPage
4

User Changes Page

dispatch(setCurrentPage(2));
New page of episodes is displayed

Search Integration

When a user searches, the filter automatically resets to “todos”:
src/App.js
const handleSearchChange = (e) => {
  dispatch(setFilter("todos"));
  dispatch(setSearchTerm(e.target.value));
};
This ensures search results include all episodes, not just the currently filtered ones.

Complete Action Reference

import {
  setFilter,      // Set active filter
  setCurrentPage  // Change page number
} from './store/slices/filterSlice';

State Persistence

The filter slice does not persist to localStorage. Filter and pagination state reset when the page is refreshed.
If you want to persist the filter state:
// Save to localStorage
localStorage.setItem('nsnCurrentFilter', currentFilter);
localStorage.setItem('nsnCurrentPage', currentPage);

// Load in initialState
initialState: {
  currentFilter: localStorage.getItem('nsnCurrentFilter') || 'todos',
  currentPage: parseInt(localStorage.getItem('nsnCurrentPage')) || 1,
  songsPerPage: 12
}

Customizing Items Per Page

The songsPerPage value is currently fixed at 12. To make it dynamic:
// Add a new action
setSongsPerPage: (state, action) => {
  state.songsPerPage = action.payload;
  state.currentPage = 1;  // Reset page when changing items per page
}

// Usage
dispatch(setSongsPerPage(24));

Filter Categories Reference

todos
string
All episodes - no filtering applied
favoritos
string
Episodes in the favoriteEpisodes array from podcast slice
empezados
string
Episodes with playbackTimes[title] > 0 from audio time slice
completados
string
Episodes in the completedEpisodes array from podcast slice
escuchar-mas-tarde
string
Episodes in the listenLaterEpisodes array from podcast slice

Build docs developers (and LLMs) love