React数据获取优化:如何避免重复调用数据库?
解决方案:React 单数据源复用实现首页搜索与直接访问详情页
针对你遇到的重复请求数据库问题,以下是几种高效的解决方案,满足首页搜索、直接访问详情页、仅加载一次数据的需求:
方案一:React Context 全局状态共享(推荐)
通过 Context 将所有笔记数据存在全局状态,所有组件共享这一份数据,仅在首次加载时请求一次数据库。
1. 创建全局笔记 Context
// NotesContext.js import { createContext, useContext, useState, useEffect } from 'react'; const NotesContext = createContext(); export const NotesProvider = ({ children }) => { const [allNotes, setAllNotes] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchNotes = async () => { try { const response = await fetch('databaseURL'); const data = await response.json(); setAllNotes(data.Items); } catch (err) { setError(err); } finally { setIsLoading(false); } }; // 仅当数据为空时请求,避免重复调用 if (allNotes.length === 0) { fetchNotes(); } }, [allNotes.length]); return ( <NotesContext.Provider value={{ allNotes, isLoading, error }}> {children} </NotesContext.Provider> ); }; // 自定义 Hook 简化 Context 调用 export const useNotes = () => useContext(NotesContext);
2. 根组件包裹 Provider
在路由外层用 NotesProvider 包裹,让所有页面都能访问全局数据:
// App.js import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import { NotesProvider } from './NotesContext'; import Home from './Home'; import Notes from './Notes'; function App() { return ( <NotesProvider> <Router> <Routes> <Route path="/" element={<Home />} /> <Route path="/notes/:noteName" element={<Notes />} /> </Routes> </Router> </NotesProvider> ); } export default App;
3. 首页组件改造
直接从 Context 取数据,不用自己请求:
// Home.js import { useNavigate } from 'react-router-dom'; import { useState } from 'react'; import { useNotes } from './NotesContext'; const Home = () => { const navigate = useNavigate(); const [searchedNote, setSearchedNote] = useState(''); const { allNotes, isLoading, error } = useNotes(); const searchNote = (event) => { event.preventDefault(); const foundNote = allNotes.find(note => note.name === searchedNote); if (foundNote) { navigate(`/notes/${foundNote.name}`); } else { alert('未找到该笔记'); } }; if (isLoading) return <div>加载中...</div>; if (error) return <div>加载失败:{error.message}</div>; return ( <div> <form onSubmit={searchNote}> <input type="text" value={searchedNote} onChange={(e) => setSearchedNote(e.target.value)} placeholder="搜索笔记名称" /> <button type="submit">搜索</button> </form> {/* 其他首页内容 */} </div> ); }; export default Home;
4. 详情页组件改造
从 Context 取全局数据,结合 URL 参数匹配目标笔记:
// Notes.js import { useParams } from 'react-router-dom'; import { useNotes } from './NotesContext'; const Notes = () => { const { noteName } = useParams(); const { allNotes, isLoading, error } = useNotes(); const note = allNotes.find(note => note.name === noteName); if (isLoading) return <div>加载中...</div>; if (error) return <div>加载失败:{error.message}</div>; if (!note) return <div>该笔记不存在</div>; return ( <div> <h1>{note.name}</h1> <p>{note.content}</p> {/* 其他详情页内容 */} </div> ); }; export default Notes;
方案二:本地存储缓存数据
如果希望页面刷新后仍能复用数据,可以结合 localStorage 缓存请求结果,减少重复请求:
// 修改 NotesContext 中的 useEffect useEffect(() => { const fetchNotes = async () => { try { // 优先读取本地缓存 const cachedNotes = localStorage.getItem('allNotes'); if (cachedNotes) { setAllNotes(JSON.parse(cachedNotes)); } // 请求最新数据并更新缓存 const response = await fetch('databaseURL'); const data = await response.json(); setAllNotes(data.Items); localStorage.setItem('allNotes', JSON.stringify(data.Items)); } catch (err) { setError(err); } finally { setIsLoading(false); } }; if (allNotes.length === 0) { fetchNotes(); } }, [allNotes.length]);
方案三:优化 API 支持单条查询
如果后端允许,新增一个根据名称获取单条笔记的接口,直接访问详情页时仅请求目标数据,无需拉取全部:
// Notes.js 改造 import { useParams, useState, useEffect } from 'react-router-dom'; import { useNotes } from './NotesContext'; const Notes = () => { const { noteName } = useParams(); const { allNotes, isLoading, error } = useNotes(); const [note, setNote] = useState(null); const [isNoteLoading, setIsNoteLoading] = useState(false); useEffect(() => { // 全局已有数据则直接匹配 if (allNotes.length > 0) { setNote(allNotes.find(n => n.name === noteName)); return; } // 全局无数据时单独请求单条笔记 const fetchSingleNote = async () => { setIsNoteLoading(true); try { const response = await fetch(`databaseURL/notes/${noteName}`); const data = await response.json(); setNote(data); } catch (err) { setError(err); } finally { setIsNoteLoading(false); } }; fetchSingleNote(); }, [allNotes, noteName]); if (isLoading || isNoteLoading) return <div>加载中...</div>; if (error) return <div>加载失败:{error.message}</div>; if (!note) return <div>该笔记不存在</div>; return ( <div> <h1>{note.name}</h1> <p>{note.content}</p> </div> ); }; export default Notes;
内容的提问来源于stack exchange,提问作者yyy
相关产品推荐
相关产品推荐

