详情页Search组件过滤失效:如何实现全局可用?
问题
我有三个React组件:Shows.jsx用于渲染全部演出列表、ProductDetails用于渲染单场演出详情,还有一个Search搜索组件实现按名称过滤演出的功能。目前Search组件在Shows页面使用时过滤功能正常,但在详情页使用时无法过滤,点击搜索后会渲染全部演出。
现有代码片段
Reducer代码
const initialState = { error: false, products: [], product: {}, allProducts: [], filteredProducts: [], }; const rootReducer = (state = initialState, action) => { const { type, payload } = action; switch (type) { case "GET_PRODUCTS": return { ...state, products: action.payload, allProducts: action.payload, filteredProducts: action.payload, }; case "SEARCH": console.log("search", action.payload); return { ...state, products: action.payload, }; } };
Action代码
export const search = (name) => { return async function (dispatch) { try { let info = await axios.get("http://localhost:3001/products/?name=" + name); console.log('info.data en action', info.data); return dispatch({ type: "SEARCH", payload: info.data, }); } catch (error) { return "No pudimos encontrar ese producto"; } }; }
Search组件代码
import React, { useState } from "react"; import "./SearchBar.css"; import { useDispatch, useSelector } from "react-redux"; import { useHistory } from "react-router-dom"; import * as actions from "../../redux/actions"; import Swal from 'sweetalert2'; import Error_Search from './Error_Search.jpg' function Search() { const [suggestions, setSuggestions] = useState([]); const [name, setName] = useState(""); const dispatch = useDispatch(); const history = useHistory(); const products = useSelector(state => state.products) const showAlertNoEnter=()=> { Swal.fire({ imageUrl: Error_Search, imageHeight: 150, imageWidth: 200, imageAlt: 'Hubo un error en la búsqueda.', title: 'Buscador de Yazz', html:'<h3>Por favor, ingresá un nombre</p>', footer:'<p>Probá de nuevo.</p>' }) } const showAlertNoName=()=> { Swal.fire({ imageUrl: Error_Search, imageHeight: 150, imageWidth: 200, imageAlt: 'Hubo un error en la búsqueda.', title: 'Buscador de Yazz', html:'<h3>Esa banda no tiene ningún show programado</p>', footer:'<p>Probá con otra banda.</p>' }) } function handleInputChange(e) { setName(e.target.value); let filtered = products.filter( (p) => p.name.toLowerCase().includes(e.target.value.toLowerCase()) ); setSuggestions(filtered); } function handleSearch(e) { e.preventDefault(); if (!name) { showAlertNoEnter(); return; } dispatch(actions.search(name)); setName(""); setSuggestions([]); history.push("/shows"); } function handleSuggestionClick(name) { dispatch(actions.search(name)); setName(""); setSuggestions([]); history.push("/shows"); } return ( <div className="searchContainer"> <div className="search_inputSuggest"> <input id="search" className="searchBar" type="text" placeholder="Buscar por nombre" onChange={(e) => handleInputChange(e)} value={name} /> <div className="search_suggestion_div"> <datalist className="suggestionsList"> {suggestions.slice(0, 10).map(s => ( <option className="suggestionsList_item" key={s.id} onClick={() => handleSuggestionClick(s.name)}> {s.name} </option> ))} </datalist> </div> </div> <button className="btnSearch" onClick={(e) => handleSearch(e)}> Buscar </button> </div> ); } export default Search;
解决方案
问题根源
- 本地过滤数据源错误:Search组件依赖
products做过滤,但进入详情页后,products状态可能不再是全量演出数据,导致搜索建议和过滤逻辑失效。 - 搜索结果被覆盖:Shows页面可能每次挂载都会调用
GET_PRODUCTS,将products重置为全量数据,覆盖搜索结果。 - Reducer状态管理混乱:SEARCH动作直接覆盖
products,没有维护独立的过滤结果状态,导致页面渲染逻辑冲突。
修复步骤
1. 更换本地过滤的数据源为全量数据
修改Search组件,使用始终存储全量数据的allProducts做本地过滤:
// Search组件中替换数据源获取逻辑 const allProducts = useSelector(state => state.allProducts) // 修改handleInputChange中的过滤逻辑 function handleInputChange(e) { setName(e.target.value); let filtered = allProducts.filter( (p) => p.name.toLowerCase().includes(e.target.value.toLowerCase()) ); setSuggestions(filtered); }
2. 调整Reducer的SEARCH动作,维护独立过滤状态
修改Reducer,让SEARCH动作更新filteredProducts而非覆盖全量的products:
case "SEARCH": console.log("search", action.payload); return { ...state, filteredProducts: action.payload, // 单独存储过滤结果 };
3. 修改Shows页面的渲染数据源
让Shows页面优先使用filteredProducts,无过滤结果时再用全量products:
// 在Shows组件中获取状态 const { products, filteredProducts } = useSelector(state => state); // 确定最终渲染的演出列表 const showsToRender = filteredProducts.length > 0 ? filteredProducts : products;
4. 避免Shows页面重复获取全量数据
修改Shows组件的GET_PRODUCTS调用逻辑,仅当全量数据为空时发起请求:
// Shows组件的useEffect示例 useEffect(() => { if (products.length === 0) { dispatch(getProducts()); // 仅全量数据为空时获取 } }, [dispatch, products.length]);
5. 优化搜索错误提示逻辑
在Action的search函数中,针对空结果或请求失败触发对应提示:
export const search = (name) => { return async function (dispatch) { try { let info = await axios.get("http://localhost:3001/products/?name=" + name); console.log('info.data en action', info.data); if (info.data.length === 0) { Swal.fire({ imageUrl: './Error_Search.jpg', imageHeight: 150, imageWidth: 200, imageAlt: 'Hubo un error en la búsqueda.', title: 'Buscador de Yazz', html:'<h3>Esa banda no tiene ningún show programado</p>', footer:'<p>Probá con otra banda.</p>' }); } return dispatch({ type: "SEARCH", payload: info.data, }); } catch (error) { Swal.fire({ imageUrl: './Error_Search.jpg', imageHeight: 150, imageWidth: 200, imageAlt: 'Hubo un error en la búsqueda.', title: 'Buscador de Yazz', html:'<h3>No pudimos realizar la búsqueda</p>', footer:'<p>Probá de nuevo más tarde.</p>' }); } }; }
内容的提问来源于stack exchange,提问作者Nico Bonder
相关产品推荐
相关产品推荐

