使用React Hooks开发会话级搜索历史页面的实现问题咨询
搜索历史功能实现方案
核心问题梳理
你现有代码存在两个核心问题:
- 组件内声明的
const history = []是普通局部变量,每次组件重渲染都会被重置,无法留存历史记录 debouncedTerm是Search组件的内部状态,无法直接在历史页组件中访问
实现步骤
1. 创建全局Context共享状态
我们用React Context实现会话级别的状态共享,页面刷新后数据自动清除,符合你的需求:
// SearchContext.js import React, { createContext, useContext, useState } from 'react'; const SearchContext = createContext(); export const SearchProvider = ({ children }) => { // 存储搜索历史 const [searchHistory, setSearchHistory] = useState([]); // 新增搜索记录的方法 const addSearchHistory = (term) => { // 可选:去重,避免同一个关键词多次出现在历史里 setSearchHistory(prev => { const filtered = prev.filter(item => item !== term); return [term, ...filtered]; // 最新搜索放在最前面 }); }; return ( <SearchContext.Provider value={{ searchHistory, addSearchHistory }}> {children} </SearchContext.Provider> ); }; // 自定义hook方便调用 export const useSearch = () => useContext(SearchContext);
然后在你的根组件(比如App.js)里包裹所有路由组件:
import { SearchProvider } from './SearchContext'; function App() { return ( <SearchProvider> {/* 你的路由/页面组件放在这里 */} </SearchProvider> ); }
2. 改造Search组件,记录搜索历史
在debouncedTerm触发搜索的useEffect里调用新增历史的方法即可:
import React, { useState, useEffect } from 'react'; import axios from 'axios'; import { useSearch } from './SearchContext'; // 引入自定义hook const Search = () => { const [term, setTerm] = useState(''); const [debouncedTerm, setDebouncedTerm] = useState(term); const [results, setResults] = useState([]); const { addSearchHistory } = useSearch(); // 获取新增历史的方法 useEffect(() => { const timerId = setTimeout(() => { setDebouncedTerm(term); }, 1000); return () => { clearTimeout(timerId); }; }, [term]); useEffect(() => { const search = async () => { const { data } = await axios.get('http://hn.algolia.com/api/v1/search?', { params: { query: debouncedTerm, }, }); setResults(data.hits); // 搜索成功后新增到历史记录 addSearchHistory(debouncedTerm); }; if (debouncedTerm) { search(); } }, [debouncedTerm, addSearchHistory]); // 后面的渲染逻辑保持不变即可 const renderedResults = results.map((result) => { return ( <div key={result.objectID} className="item"> <div className="right floated content"> <a className="ui button" href={result.url}> Go </a> </div> <div className="content"> <div className="header">{result.title}</div> </div> </div> ); }); return ( <div> <div className="ui form"> <div className="field"> <label>Hacker News Search:</label> <input value={term} onChange={(e) => setTerm(e.target.value)} className="input" /> </div> </div> <div className="ui celled list">{renderedResults}</div> </div> ); }; export default Search;
3. 实现历史页组件
直接从Context中获取历史列表渲染即可:
// SearchHistory.js import React from 'react'; import { useSearch } from './SearchContext'; const SearchHistory = () => { const { searchHistory } = useSearch(); return ( <div className="ui container"> <h3>最近搜索记录</h3> {searchHistory.length === 0 ? ( <p>暂无搜索记录</p> ) : ( <div className="ui list"> {searchHistory.map((term, index) => ( <div key={index} className="item"> {term} </div> ))} </div> )} </div> ); }; export default SearchHistory;
4. 配置路由实现页面导航
用React Router配置两个页面的路由,加简单导航栏即可:
// App.js 完整示例 import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom'; import { SearchProvider } from './SearchContext'; import Search from './Search'; import SearchHistory from './SearchHistory'; function App() { return ( <SearchProvider> <Router> <div className="ui menu"> <Link to="/" className="item">搜索页</Link> <Link to="/history" className="item">搜索历史</Link> </div> <div className="ui container"> <Routes> <Route path="/" element={<Search />} /> <Route path="/history" element={<SearchHistory />} /> </Routes> </div> </Router> </SearchProvider> ); } export default App;
内容的提问来源于stack exchange,提问作者Shiva
相关产品推荐
相关产品推荐

