如何将URL链接参数传递至请求并自动加载对应数据?
React路由参数读取问题:打开分享URL无法自动加载对应产品数据
我基于React框架开发产品列表页面,使用react-router-dom的useSearchParams生成了包含page(页码)和id(产品ID)参数的可分享URL。目前链接可正常生成,但将该链接复制到新浏览器窗口打开时,页面仍加载默认产品列表,无法自动读取URL中的参数并传递给fetchProducts函数以加载对应数据。
fetchProducts函数可根据传入的id和productPage变量向API发送请求并展示对应信息,需要实现从URL链接中获取page和id参数并传递给该函数,使得打开链接时直接展示所需数据。
原代码
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 { // 注意:此处API地址有拼写错误,多了"231" 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
解决方案
1. 从URL参数初始化状态
组件挂载时,直接从pageParams中读取page和id参数,转换为对应类型后设置到page和filterId状态中,确保初始渲染使用URL参数的值。
2. 监听URL参数变化
添加一个useEffect监听pageParams的变化,当URL参数被修改(比如直接修改地址栏),同步更新本地状态并触发数据请求。
3. 修正API地址拼写错误
原代码中产品列表的API地址多了231字符,导致请求失败,需要修正。
修改后的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>('') // 从URL参数初始化状态 const [pageParams, setPageParams] = useSearchParams() const initialPage = Number(pageParams.get('page')) || 1 const initialFilterId = pageParams.get('id') ? Number(pageParams.get('id')) : undefined const [page, setPage] = useState<number>(initialPage) const [filterId, setFilterId] = useState<number>(initialFilterId) // 监听URL参数变化,同步更新状态 useEffect(() => { const urlPage = Number(pageParams.get('page')) || 1 const urlFilterId = pageParams.get('id') ? Number(pageParams.get('id')) : undefined if (urlPage !== page) setPage(urlPage) if (urlFilterId !== filterId) setFilterId(urlFilterId) }, [pageParams]) useEffect(() => { async function fetchProducts(id?: number, productsPage = 1) { const itemsPerPage = 5 let url: string if (id) { url = `https://reqres.in/api/products/${id}` } else { // 修正API地址拼写错误 url = `https://reqres.in/api/products?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) } // 此处原代码逻辑有问题,成功时不需要设置errorMessage,注释掉 // 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
内容的提问来源于stack exchange,提问作者Yevhenii
相关产品推荐
相关产品推荐

