javascriptruby-on-railsreactjsreduxthunk

React Redux Pagination


In the Case One: Im doing a simple server-side pagination in rails and using react as front-end and redux as my state management. I have done all the things and the last thing remaining is to just pass the new generated url and fetch the new data. This data will be fetched in a another component which will generate and products. as Im using redux in my case, how am I able to pass this data to my data fetch action ?

In the Case Two: I have tried passing a parameter named url and dispatching the fetch action again with the url data i give to it. but the return is that the dispatch is not a function. Am I even able to rerun actions in action.jsx ?

action.jsx
    
    export const handlePage = (e, { activePage }) => {
        let pageNum = activePage
        let pageString = pageNum.toString();
        let url = "/api/v1/products/index/?page=" + pageString; ------> Use This ...
    }
    

export const fetchProducts = (url) => { ------> In Here
        return (dispatch) => {
            console.log(url);
            dispatch(fetchProductsRequest());
            axios
              .get(url) 
              .then((response) => {
                // response.data is the products
                const products = response.data.products;
                dispatch(fetchProductsSuccess(products));
              })
              .catch((error) => {
                // error.message is the error message
                dispatch(fetchProductsFailure(error.message));
              });
        };
    };

    export class Paginator extends React.Component {
      state = {
        page: [],
        pages: [],
      };
    
    
      componentDidMount() {
        axios
          .get("/api/v1/products/index", { withCredentials: true })
          .then((response) => {
            this.setState({
              page: response.data.page,
              pages: response.data.pages,
            });
          })
          .catch((error) => {
            console.log("Check Login Error", error);
          });
      }
    
      render() {
        return (
          <div>
            <Pagination count={this.state.pages} page={this.state.page} onChange={handlePage} />
          </div>
        );
      }
    }

Product.jsx

import React, { useEffect } from "react";
import { Link } from "react-router-dom";
import "../../style/frequentlyasked.scss";
import ItemOne from "../../files/Item-One.png";

// Redux
import { connect } from "react-redux";
import { loadCurrentItem, addToCart, fetchProducts } from "./action";

const Product = ({
  mapProducts,
  fetchProducts,
  product,
  addToCart,
  loadCurrentItem,
}) => {
  useEffect(() => {
    fetchProducts();  -----> Using it Here !
  }, []);

  return (
    <div className="card-deck d-flex justify-content-center">
      {mapProducts.map((product) => (
        <div className="card item-card" key={product.id} product={product}>
          {/* Admin Card */}
          {/* Header Image */}
          <img className="card-img-top" src={ItemOne} alt="Card image cap" />
          {/* Card Body */}
          <div className="card-body">
            <h4 className="card-title">{product.title}</h4>
            <h5 className="card-title">$ {product.price}</h5>
            <p className="card-text">{product.description}</p>
            <button
              className="btn btn-primary"
              onClick={() => addToCart(product.id)}
            >
              + Add To Cart
            </button>
            <a href="#" className="btn btn-danger">
              <svg
                width="1em"
                height="1em"
                viewBox="0 0 16 16"
                className="bi bi-heart-fill"
                fill="currentColor"
                xmlns="http://www.w3.org/2000/svg"
              >
                <path
                  fillRule="evenodd"
                  d="M8 1.314C12.438-3.248 23.534 4.735 8 15-7.534 4.736 3.562-3.248 8 1.314z"
                />
              </svg>
            </a>
          </div>
          {/* Card Footer */}
          <div className="card-footer">
            <small className="text-muted">Last updated 3 mins ago</small>
          </div>
        </div>
      ))}
    </div>
  );
};

const mapStateToProps = (state) => {
  return {
    mapProducts: state.shop.products,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    addToCart: (id) => dispatch(addToCart(id)),
    loadCurrentItem: (item) => dispatch(loadCurrentItem(item)),
    fetchProducts: () => dispatch(fetchProducts()),
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(Product);

Solution

  • In your case:

    
      useEffect(() => {
        fetchProducts(page);  -----> Using it Here !
      }, [page]);
    
    //and
    
    
        fetchProducts: (page) => dispatch(fetchProducts(page)),
    
    

    Note that your mapDispatchToProps could (and should) also be written in the map object notation:

    const mapDispatchToProps = {
      addToCart,
      loadCurrentItem,
      fetchProducts
    }
    

    Also note that the official recommendation is to use the react-redux hooks instead of connect and mapDispatchToProps.

    So skip the whole connect stuff and in your component:

    const Product = ({
      product,
    }) => {
      const mapProducts = useSelector(state => state.shop.products)
      const dispatch = useDispatch()
      useEffect(() => {
        dispatch(fetchProducts(page));
      }, []);
    
    

    Also, if you are still using connect, you have probable been following outdated tutorials. Redux has changed a lot over the last few years. Look into modern redux and the up-to-date official redux tutorials

    By the way: there is a new API on the way for the official redux toolkit which can take care of all that data fetching for you. You can already try it out, at the moment as an extra package: https://rtk-query-docs.netlify.app/