DEV Community

Pedro Alvarado
Pedro Alvarado

Posted on • Edited on

Cursor pagination con Redux Toolkit

La idea es manejar:

  • items → los resultados de la página actual.
  • nextCursor → el cursor para pedir la siguiente página.
  • previousCursor → el cursor para volver atrás.
  • sort → el orden aplicado a la búsqueda.
  • loading / error → estados de red.

1. Slice de Redux

// store/paginationSlice.js import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; // thunk para traer resultados de la API usando cursor export const fetchPage = createAsyncThunk( "pagination/fetchPage", async ({ cursor, sort }, thunkAPI) => { // Ejemplo de API con cursor y sort const response = await fetch( `/api/items?cursor=${cursor || ""}&sort=${sort}` ); const data = await response.json(); return data; // Supongamos que data = { items: [...], nextCursor: "abc", previousCursor: "xyz" } } ); const paginationSlice = createSlice({ name: "pagination", initialState: { items: [], nextCursor: null, previousCursor: null, sort: "docdate:asc", loading: false, error: null, }, reducers: { changeSorting: (state, action) => { state.sort = action.payload; }, }, extraReducers: (builder) => { builder .addCase(fetchPage.pending, (state) => { state.loading = true; state.error = null; }) .addCase(fetchPage.fulfilled, (state, action) => { state.loading = false; state.items = action.payload.items; state.nextCursor = action.payload.nextCursor; state.previousCursor = action.payload.previousCursor; }) .addCase(fetchPage.rejected, (state, action) => { state.loading = false; state.error = action.error.message; }); }, }); export const { changeSorting } = paginationSlice.actions; export default paginationSlice.reducer; 
Enter fullscreen mode Exit fullscreen mode

2. Configurar el Store

// store/index.js import { configureStore } from "@reduxjs/toolkit"; import paginationReducer from "./paginationSlice"; export const store = configureStore({ reducer: { pagination: paginationReducer, }, }); 
Enter fullscreen mode Exit fullscreen mode

3. Proveedor en la App

// main.jsx import React from "react"; import ReactDOM from "react-dom/client"; import { Provider } from "react-redux"; import { store } from "./store"; import App from "./App"; ReactDOM.createRoot(document.getElementById("root")).render( <Provider store={store}> <App /> </Provider> ); 
Enter fullscreen mode Exit fullscreen mode

4. Componente con Paginación y Ordenamiento

// App.jsx import React, { useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; import { fetchPage, changeSorting } from "./store/paginationSlice"; export default function App() { const { items, nextCursor, previousCursor, sort, loading, error } = useSelector( (state) => state.pagination ); const dispatch = useDispatch(); // Primera carga useEffect(() => { dispatch(fetchPage({ cursor: null, sort })); }, [dispatch, sort]); const handleNext = () => { if (nextCursor) dispatch(fetchPage({ cursor: nextCursor, sort })); }; const handlePrevious = () => { if (previousCursor) dispatch(fetchPage({ cursor: previousCursor, sort })); }; const handleSortChange = (e) => { dispatch(changeSorting(e.target.value)); dispatch(fetchPage({ cursor: null, sort: e.target.value })); }; return ( <div> <h1>Resultados</h1> {loading && <p>Cargando...</p>} {error && <p>Error: {error}</p>} <select value={sort} onChange={handleSortChange}> <option value="docdate:asc">Fecha ↑</option> <option value="docdate:desc">Fecha ↓</option> <option value="slug:asc">Slug ↑</option> <option value="slug:desc">Slug ↓</option> <option value="score:asc">Score ↑</option> <option value="score:desc">Score ↓</option> </select> <ul> {items.map((item) => ( <li key={item.id}>{item.title}{item.date}</li> ))} </ul> <div> <button disabled={!previousCursor} onClick={handlePrevious}> ← Anterior </button> <button disabled={!nextCursor} onClick={handleNext}> Siguiente → </button> </div> </div> ); } 
Enter fullscreen mode Exit fullscreen mode

5. Flujo completo (en pseudocódigo 🧠)

  1. Usuario abre la página → useEffect dispara fetchPage({cursor: null, sort}).
  2. Redux hace pending → fulfilled → llena items, nextCursor, previousCursor.
  3. Usuario da click en Siguiente → manda fetchPage({cursor: nextCursor, sort}).
  4. API responde con la siguiente página → Redux actualiza items y cursores.
  5. Usuario cambia orden → changeSorting guarda nuevo sort y vuelve a pedir página inicial.

Top comments (0)