MERN栈中如何携带新参数发起GET请求实现数据筛选并无刷新更新?
问题解决与实现方案
一、解决Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body错误
原因分析
GET/HEAD请求的HTTP规范不允许携带请求体(body),你当前后端filterProject函数从req.body读取参数,同时路由配置为GET /filter,如果前端用GET请求携带body就会触发该错误。以下是两种可行修改方案:
方案1:改用POST请求(适配现有代码)
后端修改
- 修改路由文件,将
GET /filter改为POST /filter:
// 路由文件 // 替换原有GET /filter路由 router.post('/filter', filterProject)
- 后端
filterProject函数无需修改(已从req.body取参)
前端修改
通过父子组件传值,让Dropdown提交时调用父组件的筛选请求函数:
- 修改
FilterBody组件,新增筛选请求函数并传递给Dropdown:
// Filterpage.js const FilterBody = () => { const [projects, setProjects] = useState(null) // 新增:获取筛选后的项目数据 const fetchFilteredProjects = async (filterData) => { const response = await fetch('/api/projects/filter', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(filterData) }) const json = await response.json() if (response.ok) { setProjects(json) } } useEffect(() => { const fetchProjects = async () => { const response = await fetch('/api/projects') const json = await response.json() if (response.ok) { setProjects(json) } } fetchProjects() }, []) return ( <div className="filterHome"> <div className="filterTableContainer"> <div className="filterTableTitle"> Filter Table </div> <div className="filterSDGDropDown"> {/* 传递筛选函数给Dropdown */} <Dropdown onFilterSubmit={fetchFilteredProjects} /> </div> </div> {/* 项目列表部分保持不变 */} <div> <div className="projects"> {projects && projects.map((project) => ( <ProjectDetails key={project._id} project={project}/> ))} </div> </div> </div> ) }
- 修改
Dropdown组件的handleSubmit方法,调用父组件传来的函数:
// Dropdown.js // 修改handleSubmit方法 handleSubmit(event) { event.preventDefault(); const { sdg, assignment_type, theme } = this.state; // 触发父组件的筛选请求 this.props.onFilterSubmit({ sdg, assignment_type, theme }); }
方案2:使用GET请求的查询参数(符合REST语义)
如果希望保持GET请求(筛选属于数据查询操作,GET语义更匹配),可以将参数放在URL的查询字符串中:
后端修改
- 保持
GET /filter路由不变,修改filterProject函数从req.query取参:
// controller中的filterProject函数 const filterProject = async (req, res) => { // 从查询字符串获取参数,替代req.body const {sdg, assignment_type, theme} = req.query const filteredProjects = await Project.find({sdg, assignment_type, theme}) res.status(200).json(filteredProjects) }
前端修改
修改FilterBody中的筛选请求函数,用查询字符串传递参数:
// Filterpage.js中的fetchFilteredProjects const fetchFilteredProjects = async (filterData) => { // 将筛选参数转为查询字符串 const searchParams = new URLSearchParams(filterData); const response = await fetch(`/api/projects/filter?${searchParams}`) const json = await response.json() if (response.ok) { setProjects(json) } }
Dropdown组件的其他修改与方案1一致,调用父组件的fetchFilteredProjects即可。
二、React Context实现无刷新更新结果
完全可以用React Context实现,尤其适合后续组件层级加深、不想层层传递props的场景,具体实现步骤如下:
1. 创建Project Context
新建ProjectContext.js文件:
import { createContext, useContext, useState } from 'react'; const ProjectContext = createContext(); export const ProjectProvider = ({ children }) => { const [projects, setProjects] = useState(null); // 获取全部项目 const fetchAllProjects = async () => { const response = await fetch('/api/projects'); const json = await response.json(); if (response.ok) { setProjects(json); } }; // 获取筛选后的项目(示例用POST方案,也可替换为GET查询参数方案) const fetchFilteredProjects = async (filterData) => { const response = await fetch('/api/projects/filter', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(filterData) }); const json = await response.json(); if (response.ok) { setProjects(json); } }; return ( <ProjectContext.Provider value={{ projects, fetchAllProjects, fetchFilteredProjects }}> {children} </ProjectContext.Provider> ); }; // 自定义Hook,简化Context调用 export const useProjectContext = () => { return useContext(ProjectContext); };
2. 在根组件中包裹Provider
比如在App.js中:
import { ProjectProvider } from './context/ProjectContext'; function App() { return ( <ProjectProvider> {/* 包含FilterBody在内的所有子组件 */} <FilterBody /> </ProjectProvider> ); }
3. 修改FilterBody组件使用Context
// Filterpage.js import { useEffect } from 'react'; import { useProjectContext } from '../context/ProjectContext'; import ProjectDetails from '../ProjectDetails'; import Dropdown from './Dropdown'; const FilterBody = () => { const { projects, fetchAllProjects } = useProjectContext(); useEffect(() => { fetchAllProjects(); }, [fetchAllProjects]); return ( <div className="filterHome"> <div className="filterTableContainer"> <div className="filterTableTitle">Filter Table</div> <div className="filterSDGDropDown"> <Dropdown /> </div> </div> <div> <div className="projects"> {projects && projects.map((project) => ( <ProjectDetails key={project._id} project={project} /> ))} </div> </div> </div> ); }; export default FilterBody;
4. 修改Dropdown组件使用Context
将类组件改为函数组件,更方便使用Hook:
// Dropdown.js import { useState } from 'react'; import { useProjectContext } from '../context/ProjectContext'; const Dropdown = () => { const { fetchFilteredProjects } = useProjectContext(); const [sdg, setSdg] = useState('SDG 1: No Poverty'); const [assignment_type, setAssignmentType] = useState('1'); const [theme, setTheme] = useState('Demographic'); const handleSubmit = (e) => { e.preventDefault(); fetchFilteredProjects({ sdg, assignment_type, theme }); }; return ( <form onSubmit={handleSubmit}> <label>SDG:</label> <select value={sdg} onChange={(e) => setSdg(e.target.value)}> <option value="SDG 1: No Poverty">SDG 1: No Poverty</option> <option value="SDG 2: Zero Hunger">SDG 2: Zero Hunger</option> <option value="SDG 3: Good Health & Well Being">SDG 3: Good Health & Well Being</option> </select> <label>Assignment Type:</label> <select value={assignment_type} onChange={(e) => setAssignmentType(e.target.value)}> <option value="1">1: Discussion Project</option> <option value="2">2: PDF Case study</option> <option value="3">3: Community Project</option> </select> <label>Theme:</label> <select value={theme} onChange={(e) => setTheme(e.target.value)}> <option value="Demographic">Demographic</option> <option value="Economical">Economical</option> <option value="Socio-cultural">Socio-cultural</option> <option value="Technological">Technological</option> <option value="Ecological">Ecological</option> <option value="Poltical">Poltical</option> </select> <input type="submit" value="Submit" /> </form> ); }; export default Dropdown;
点击提交后,Context会自动更新projects状态,FilterBody中的项目列表会重新渲染,无需刷新页面。
内容的提问来源于stack exchange,提问作者Soccerball123
相关产品推荐
相关产品推荐

