Home > Blockchain >  How to pass parameters from a URL link to a request?
How to pass parameters from a URL link to a request?

Time:01-18

In my Main.tsx:

import React, { FC, useEffect, useState } from 'react'
import { useSearchParams } from 'react-router-dom'

import { useAppDispatch, useAppSelector } from '../../hook'
import { getProducts } from '../../store/ProductsSlice'

import Filter from '../Filter/Filter'
import Pagination from '../Pagination/Pagination'
import Products from '../Products/Products'

import { ErrorMessage, FilterError } from './styled'

const Main: FC = () => {
    const products = useAppSelector((state) => state.products.list)

    const dispatch = useAppDispatch()

    const [errorId, setErrorId] = useState<string>('')
    const [errorMessage, setErrorMessage] = useState<string>('')

    const [page, setPage] = useState<number>(1)
    const [filterId, setFilterId] = useState<number>()

    const [pageParams, setPageParams] = useSearchParams()
    pageParams.get(`page`) || ''
    pageParams.get(`id`) || ''

    useEffect(() => {
        async function fetchProducts(id?: number, productsPage = 1) {
            const itemsPerPage = 5
            let url: string
            if (id) {
                url = `https://reqres.in/api/products/${id}`
            } else {
                url = `https://reqres.in/api/pr231oducts?per_page=${itemsPerPage}&page=${productsPage}`
            }
            const requestOptions = {
                method: 'GET',
                headers: { 'Content-Type': 'application/json' },
            }

            fetch(url, requestOptions)
                .then(async (response) => {
                    const data = await response.json()
                    if (response.ok) {
                        setErrorId('')
                        setErrorMessage('')
                        if (id) {
                            dispatch(
                                getProducts({
                                    page: 1,
                                    per_page: 1,
                                    total: 1,
                                    total_pages: 1,
                                    data: [data.data],
                                })
                            )
                            setPageParams({ page: `1`, id: `${id}` })
                        } else {
                            dispatch(getProducts(data))
                            setPageParams({ page: `${productsPage}` })
                        }
                    } else {
                        const error = (data && data.message) || response.status
                        return Promise.reject(error)
                    }
                    setErrorMessage(data.id)
                })
                .catch((error) => {
                    setErrorId(error.toString())
                    console.error('There was an error!', error)
                })
        }
        fetchProducts(filterId, page)
    }, [filterId, page])

    return (
        <div>
            {!products ? (
                <>
                    {errorId ? <ErrorMessage>{errorId}</ErrorMessage> : null}
                    {errorMessage ? (
                        <ErrorMessage>
                            Something went wrong
                            {errorMessage}
                        </ErrorMessage>
                    ) : null}
                </>
            ) : (
                <>
                    <Filter setFilterId={setFilterId} />
                    {errorId ? (
                        <FilterError>
                            {errorId}:
                            {errorId === '404'
                                ? ' Product not found'
                                : `${errorId}: ${errorMessage}`}
                        </FilterError>
                    ) : (
                        <Products />
                    )}

                    <Pagination setPage={setPage} />
                </>
            )}
        </div>
    )
}

export default Main

Filter.tsx:

import React, { FC } from 'react'

import { FilterContainer, FilterInput } from './styled'

const Filter: FC<{
    setFilterId: React.Dispatch<React.SetStateAction<number | undefined>>
}> = ({ setFilterId }) => {
    return (
        <FilterContainer>
            <FilterInput
                onChange={(e) => {
                    if (e.target.value === '0') {
                        e.target.value = ''
                    }
                    setFilterId(Number(e.target.value))
                }}
                placeholder="Search by id"
                type="number"
            />
        </FilterContainer>
    )
}

export default Filter

Pagination.tsx:

import { FC } from 'react'
import { useAppSelector } from '../../hook'

import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'

import { PaginationBtn, PaginationContainer } from './styled'

const Pagination: FC<{
    setPage: React.Dispatch<React.SetStateAction<number>>
}> = ({ setPage }) => {
    let pageNumber = useAppSelector((state) => state.products.list.page)
    const totalPages = useAppSelector((state) => state.products.list.total_pages)

    return (
        <PaginationContainer>
            <PaginationBtn
                onClick={() => {
                    setPage((pageNumber -= 1))
                }}
                disabled={pageNumber <= 1}
            >
                <ArrowBackIosIcon fontSize="large" />
            </PaginationBtn>

            <PaginationBtn
                onClick={() => {
                    setPage((pageNumber  = 1))
                }}
                disabled={pageNumber >= totalPages}
            >
                <ArrowForwardIosIcon fontSize="large" />
            </PaginationBtn>
        </PaginationContainer>
    )
}

export default Pagination

The fetchProducts function makes a request to the API, using the productPage and id variables passed to the function, the corresponding request is sent and the necessary information is displayed on the screen. I'm going to take the page and id from the link and pass them to the fetchProducts function so that if something happens, the site opens immediately with the necessary information.

I have useSearchParams() with which I make a link that can be "sent to other users". But I don’t understand how to implement that when parameters are inserted into the link, they are applied and the page with the necessary data is loaded.

Now the correct link is generated, but if you copy it and paste it in another browser window, the standard "list of products" will be loaded

CodePudding user response:

I have already an exemple for make you understand How to pass parameters from a URL link to a request:

App.js

function App() {
  return (
    <>
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/add-book" element={<AddBook />} />
        <Route path="/upd-book/:id" element={<UpdBook />} />
      </Routes>
    </BrowserRouter>
    </>
  )
}

export default App;

Home.js

<Link to={`/upd-book/${id}`} >Update</Link>

UpdatePage.js exemple url after you click to Link: localhost:3000/upd-book/30

import {useParams} from 'react-router-dom';

const {id} = useParams();

{id} is 30

I hope this exemple explain how this is work.

CodePudding user response:

Issue

The Main component has competing "sources of truth", the queryString params and local state.

Solution

Use the queryString params as the source of truth for the API requests. Access the "page" and "id" query params in the component and pass as useEffect hook dependencies and on to the fetchProducts handler. Instead of enqueuing state updates enqueue navigation redirects that only update the URL queryString.

const Main: FC = () => {
  const dispatch = useAppDispatch();
  const products = useAppSelector((state) => state.products.list);

  const [searchParams, setSearchParams] = useSearchParams();

  // Read the queryString parameters, convert to number type
  const page = Number(searchParams.get("page") || 1);
  const filterId = Number(searchParams.get("id"));
    
  const [errorId, setErrorId] = useState<string>('');
  const [errorMessage, setErrorMessage] = useState<string>('');
  useEffect(() => {
    async function fetchProducts(id?: number, page: number = 1) {
      const itemsPerPage = 5;
      const url = id
        ? `https://reqres.in/api/products/${id}`
        : `https://reqres.in/api/pr231oducts?per_page=${itemsPerPage}&page=${page}`;

      const requestOptions = {
        method: 'GET',
        headers: { 'Content-Type': 'application/json' },
      }

      try {
        const response = await fetch(url, requestOptions);
        const data = await response.json();
      
        if (response.ok) {
          setErrorId('');
          setErrorMessage('');

          if (id) {
            dispatch(
              getProducts({
                page: 1,
                per_page: 1,
                total: 1,
                total_pages: 1,
                data: [data.data],
              })
            );
            setSearchParams({ page, id }, { replace: true });
          } else {
            dispatch(getProducts(data));
            setPageParams({ page }, { replace: true });
          }
        } else {
          const error = data?.message || response.status;
          return Promise.reject(error);
        }
        setErrorMessage(data.id);
      } catch(error) {
        setErrorId(error.toString());
        console.error('There was an error!', error);
      }
    };

    fetchProducts(filterId, page);
  }, [filterId, page]);
  // Callbacks to update the queryString parameters
  const setPage = page => setSearchParams(params => {
    params.set("page", page);
    return params;
  }, { replace: true });

  const setFilterId = id => setSearchParams(params => {
    params.set("id", id);
    return params;
  }, { replace: true });
  return (
    <div>
      {!products ? (
        <>
          {errorId ? <ErrorMessage>{errorId}</ErrorMessage> : null}
          {errorMessage && (
            <ErrorMessage>
              Something went wrong
              {errorMessage}
            </ErrorMessage>
          )}
        </>
      ) : (
        <>
          <Filter setFilterId={setFilterId} />
          {errorId ? (
            <FilterError>
              {errorId}:
              {errorId === '404'
                ? "Product not found"
                : `${errorId}: ${errorMessage}`
              }
            </FilterError>
          ) : (
            <Products />
          )}

          <Pagination setPage={setPage} />
        </>
      )}
    </div>
  );
};
  • Related