React Router 6搜索参数问题:输入时避免实时修改URL
解决React Router v6搜索输入时实时修改URL的问题
核心思路是用本地状态管理输入框的实时值,仅在表单提交时才更新URL的搜索参数,同时保留直接访问带参数URL的功能。具体修改如下:
1. 新增本地状态管理输入值
在Header组件中,用useState维护输入框的实时内容,初始值从URL搜索参数中读取:
const [inputValue, setInputValue] = useState(searchParams.get('query') || '')
2. 修改输入框的绑定逻辑
将输入框的value绑定到本地状态inputValue,onChange仅更新本地状态,不再直接修改URL:
<input className='header--form--input' autoComplete='off' type='text' placeholder='Search' onChange={(e) => setInputValue(e.target.value)} // 仅更新本地状态 name='input' value={inputValue} // 绑定本地状态 />
3. 调整表单提交逻辑
提交时再更新搜索参数、导航并发起请求:
const handleSubmit = async (event) => { event.preventDefault() if (!inputValue.trim()) return // 空输入不处理 // 更新搜索参数 setSearchParams({ query: inputValue }) // 发起请求 await fetchImages(inputValue) // 导航到搜索页(可选,若当前已在/search页可省略) navigate(`/search?query=${inputValue}`) }
4. 适配直接访问带参数URL的场景
添加useEffect监听URL搜索参数的变化,同步到本地输入状态,确保直接访问URL时输入框显示正确内容:
useEffect(() => { const urlQuery = searchParams.get('query') || '' if (urlQuery !== inputValue) { setInputValue(urlQuery) // 若当前在搜索页且输入值变化,自动发起请求 if (location.pathname === '/search') { fetchImages(urlQuery) } } }, [searchParams, location.pathname])
5. 修改fetchImages函数
让它接收查询参数作为参数,避免依赖组件内的query变量:
async function fetchImages(query) { if (!query.trim()) return try { const res = await fetch(`https://api.unsplash.com/search/photos?page=${page}&per_page=30&query=${query}&client_id=${process.env.REACT_APP_UNSPLASH_API_KEY}`) const data = await res.json() if (data.total !== 0) { setAllImages(data.results) setTotalResults(data.total) } } catch(error) { setError(error) } }
完整修改后的Header组件代码
import React from 'react' import Navbar from './Navbar' import create from 'zustand' import { useState, useEffect } from 'react' import { useLocation, useNavigate, useSearchParams } from 'react-router-dom' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faTriangleExclamation } from '@fortawesome/free-solid-svg-icons' // Zustand let store = (set) => ({ allImages: [], setAllImages: (images) => set({ allImages: images}), totalResults: null, setTotalResults: (num) => set({ totalResults: num}), }) export const useMain = create(store) function Header() { const [error, setError] = useState(null) const [showError, setShowError] = useState(false) const [fadeOut, setFadeOut] = useState(false) const [page, setPage] = useState(1) const [searchParams, setSearchParams] = useSearchParams() // 本地状态管理输入值,初始从URL参数读取 const [inputValue, setInputValue] = useState(searchParams.get('query') || '') const allImages = useMain(state => state.allImages) const setAllImages = useMain(state => state.setAllImages) const setTotalResults = useMain(state => state.setTotalResults) async function fetchImages(query) { if (!query.trim()) return try { const res = await fetch(`https://api.unsplash.com/search/photos?page=${page}&per_page=30&query=${query}&client_id=${process.env.REACT_APP_UNSPLASH_API_KEY}`) const data = await res.json() if (data.total !== 0) { setAllImages(data.results) setTotalResults(data.total) } } catch(error) { setError(error) } } let navigate = useNavigate() const handleSubmit = async (event) => { event.preventDefault() if (!inputValue.trim()) return setSearchParams({ query: inputValue }) await fetchImages(inputValue) navigate(`/search?query=${inputValue}`) } const location = useLocation() // 监听URL参数变化,同步到输入框并自动请求 useEffect(() => { const urlQuery = searchParams.get('query') || '' if (urlQuery !== inputValue) { setInputValue(urlQuery) if (location.pathname === '/search') { fetchImages(urlQuery) } } }, [searchParams, location.pathname, inputValue]) // error useEffect(() => { if (error) { setShowError(true) setTimeout(() => { setFadeOut(true) setTimeout(() => { setShowError(false) }, 1000) }, 5000) } }, [error]) return ( <div className='header'> <Navbar /> <h2 className='header--heading text-center text-light'>Find Images</h2> <div className='header--form'> <form onSubmit={handleSubmit}> <input className='header--form--input' autoComplete='off' type='text' placeholder='Search' onChange={(e) => setInputValue(e.target.value)} name='input' value={inputValue} /> </form> </div> {showError && <div className={`network-error ${fadeOut ? 'fade-out' : ''}`}> <i><FontAwesomeIcon icon={faTriangleExclamation} /></i> <div className='network-error--message'> <h5>Network Error</h5> <p>Please check your Internet connection and try again</p> </div> </div>} </div> ) } export default Header
修改后效果:
- 用户输入时仅更新本地状态,URL不会实时变化
- 提交表单时才更新URL搜索参数并发起请求
- 直接访问带
query参数的URL时,输入框会自动填充参数内容并发起搜索
内容的提问来源于stack exchange,提问作者haharimoto
相关产品推荐
相关产品推荐

