'How to make reusable pagination with react and redux?

On my application i'm using Reduxjs/toolkit for state management and TypeScript for type safety. My backend were wrote in Node.js with MongoDB.

I have implemented pagination for one slice/component, and i know that is not the best solution and i want to improve it and make reusable for other slices.

Could you help me with that? Give me some hints?

Below is my current pagination implementation:

// CategorySlice

interface InitialState {
  categories: ICategory[];
  isFetching: boolean;
  errorMessage: string | null;

  // to refactor
  currentPage: number;
  itemsPerPage: number;
  totalResults: number;
}

const initialState: InitialState = {
  categories: [],
  isFetching: false,
  errorMessage: '',

  // to refactor
  currentPage: 1,
  itemsPerPage: 9,
  totalResults: 0,
};

export const fetchCategories = createAsyncThunk<
  { data: ICategory[]; totalResults: number },
  number
>('category/fetchCategories', async (currentPage, { rejectWithValue }) => {
  try {
    const accessToken = getToken();
    if (!accessToken) rejectWithValue('Invalid token');
    const config = {
      headers: { Authorization: `Bearer ${accessToken}` },
    };
    const response: IApiResponse<ICategoryToConvert[]> = await api.get(
      `/categories?page=${currentPage}&limit=9&isPrivate[ne]=true`,
      config
    );
    const data = response.data.data;
    const convertedData = data.map(e => {
      return {
        id: e._id,
        name: e.name,
        image: e.image,
      };
    });
    return {
      totalResults: response.data.totalResults,
      data: convertedData,
    };
  } catch (error) {
    removeToken();
    return rejectWithValue(error);
  }
});

export const categorySlice = createSlice({
  name: 'category',
  initialState,
  reducers: {
    setNextPage(state, { payload }) {
      state.currentPage = payload;
    },
  },
  extraReducers: builder => {
    builder.addCase(fetchCategories.pending, state => {
      state.isFetching = true;
      state.errorMessage = null;
    });
    builder.addCase(fetchCategories.fulfilled, (state, action) => {
      state.categories = action.payload.data;
      state.isFetching = false;
      state.totalResults = action.payload.totalResults;
    });
    builder.addCase(fetchCategories.rejected, state => {
      state.isFetching = false;
      state.errorMessage = 'Problem with fetching categories 🐱‍👤';
    });
  },
});

// Category Page

const CategoryPage = () => {
  const dispatch = useAppDispatch();
  const { currentPage } = useAppSelector(state => state.category);

  useEffect(() => {
    dispatch(fetchCategories(currentPage));
  }, [dispatch, currentPage]);
  return (
    <ContainerWrapper>
      <CategoryList />
    </ContainerWrapper>
  );
};

export default CategoryPage;

Inside CategoryPage I'm passing those properties from state selector.

  <Pagination
    currentPage={currentPage}
    itemsPerPage={itemsPerPage}
    paginate={(n: number) => dispatch(categoryActions.setNextPage(n))}
    totalItems={totalResults}
  />

And finally PaginationComponent

interface IProps {
  itemsPerPage: number;
  totalItems: number;
  paginate: (numb: number) => void;
  currentPage: number;
}

const Pagination = ({ itemsPerPage, totalItems, paginate, currentPage }: IProps) => {
  const numberOfPages = [];
  for (let i = 1; i <= Math.ceil(totalItems / itemsPerPage); i++) {
    numberOfPages.push(i);
  }

  return (
    <nav className={styles['pagination']}>
      <ul className={styles['pagination__list']}>
        {numberOfPages.map(number => {
          return (
            <li
              key={number}
              className={`${styles['pagination__item']} ${
                currentPage === number && styles['pagination__item--active']
              }`}
              onClick={() => paginate(number)}
            >
              <div className={styles['pagination__link']}>{number}</div>
            </li>
          );
        })}
      </ul>
    </nav>
  );
};

export default Pagination;


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source