You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 10:55:20