使用React Hook实现筛选下拉框同步URL与选中状态问题咨询
问题核心原因
Dropdown.Item的href属性与onClick事件冲突,点击时href的hash跳转优先级更高,会打断React状态更新流程,导致两者无法同时生效- 下拉组件内部单独维护了
genreDropDown状态,和全局filterField、路由参数没有同步,多数据源导致显示不一致 - 没有监听路由参数变化,手动修改URL时筛选状态不会同步更新
- 路由定义冗余,手动拼接路径的写法容错性极低
具体修改方案
1. 先修改App组件
- 引入路由hook监听参数、实现编程式导航
- 统一用路由参数作为筛选的唯一数据源,避免多状态冲突
- 优化路由定义,删除冗余逻辑
修改后代码:
import React, { useState, useEffect } from 'react'; import Navi from './Navi' import CardGroup from './CardGroup' import SearchBox from './SearchBox' import './App.css'; import games from './csvjson.json'; import Content from './Content'; import FilterDropDown from './FilterDropDown' import { HashRouter as Router, Route, useHistory, useParams } from "react-router-dom"; // 把页面渲染逻辑抽成单独组件,方便使用路由hook function MainPage() { const [searchfield, setSearchfield] = useState('') const [filterField, setFilterField] = useState('') const history = useHistory() const { genre } = useParams() // 读取路由里的分类参数 // 路由变化时同步更新筛选值 useEffect(() => { if(genre) setFilterField(genre) }, [genre]) const onSearchChange = (event) => { setSearchfield(event.target.value) } // 筛选变化时同步更新路由和本地状态 const onFilterChange = (filterGenre) => { setFilterField(filterGenre) history.push(`/Genre/${filterGenre}`) // 编程式导航修改URL,不会打断状态更新 } const filteredGames = games.filter(game => { return (game.Title.toLowerCase().includes(searchfield.toLowerCase()) && game.genre.toLowerCase().includes(filterField.toLowerCase())) }) return ( <div> <Navi /> <SearchBox searchChange={onSearchChange}/> {/* 把当前选中值传给下拉组件,不需要下拉自己维护状态 */} <FilterDropDown onFilterChange={onFilterChange} selectedGenre={filterField} /> <CardGroup games={filteredGames}/> </div> ) } function App() { return ( <Router> <Route path="/" exact component={MainPage}/> <Route path="/games/:games" component={Content} /> {/* 动态路由匹配所有分类路径 */} <Route path="/Genre/:genre" component={MainPage} /> </Router> ); } export default App;
2. 再修改FilterDropDown组件
- 删除内部私有状态,直接用父组件传入的选中值作为显示内容
- 删掉Dropdown.Item的href属性,避免和点击事件冲突
修改后代码:
import React from 'react' import { Dropdown } from 'react-bootstrap'; function FilterDropDown ({onFilterChange, selectedGenre}) { const handleSelect=(e)=>{ console.log(e); } return ( <Dropdown className="text-center m-3 p-auto"> <Dropdown.Toggle variant="success" id="dropdown-basic"> {/* 直接显示父组件传过来的选中值,和全局状态同步 */} {selectedGenre || '请选择分类'} </Dropdown.Toggle> <Dropdown.Menu> {/* 删掉href属性,只保留点击事件 */} <Dropdown.Item onClick={() => onFilterChange("Action")}>Action</Dropdown.Item> <Dropdown.Item onClick={() => onFilterChange("Adventure")}>Adventure</Dropdown.Item> <Dropdown.Item onClick={() => onFilterChange("Role-Playing")}>Role-Playing</Dropdown.Item> </Dropdown.Menu> </Dropdown> ) } export default FilterDropDown
内容的提问来源于stack exchange,提问作者Forged
相关产品推荐
相关产品推荐

