You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免向后端发送无限GET请求同时实现前端动态渲染?

问题分析与解决方案

问题根源

  1. 无限GET请求循环:你的useEffect将requiredFields设为依赖项,但在fetchData函数中又调用setRequiredFields更新该状态,导致useEffect被反复触发,无限发起请求。
  2. 移除依赖后不更新:移除requiredFields依赖后,useEffect仅在组件挂载时执行一次。当子组件(如TitleForm)修改课程数据后,父组件没有触发重新拉取数据的逻辑,因此必须刷新页面才能看到新值。

具体修复步骤

1. 用useMemo替代状态计算完成度相关值

totalFields、completedFields、completionText完全依赖course的数据,无需单独存为状态,用useMemo实时计算即可,避免不必要的状态更新:

import React, { useEffect, useState, useMemo } from 'react'
// ... 其他导入代码保持不变

const Course = () => {
    const { courseId } = useParams();
    const navigate = useNavigate()
    const [course, setCourse] = useState(null);
    const [isLoading, setIsLoading] = useState(true)

    // 用useMemo实时计算完成度相关值
    const totalFields = useMemo(() => {
        if (!course) return 0;
        return [course.title, course.description, course.imageLink, course.price].length;
    }, [course]);

    const completedFields = useMemo(() => {
        if (!course) return 0;
        return [course.title, course.description, course.imageLink, course.price].filter(Boolean).length;
    }, [course]);

    const completionText = useMemo(() => `${completedFields}/${totalFields}`, [completedFields, totalFields]);

    // ... 后续代码继续修改
}

2. 修正useEffect的依赖与逻辑

移除requiredFields依赖,仅保留courseId和navigate(仅在组件挂载或courseId变化时拉取数据),同时删除setRequiredFields相关代码:

useEffect(() => {
    const fetchData = async () => {
        try {
            setIsLoading(true);
            const response = await fetch(`http://localhost:3000/instructor/courses/${courseId}`, {
                method: 'GET',
                headers: {
                    authorization: `Bearer ${localStorage.getItem('token')}`
                }
            });

            if (response.status === 401) {
                return navigate("/signin")
            }

            if (!response.ok) {
                throw new Error('Failed to fetch course data');
            }

            const data = await response.json();
            setCourse(data.course);
            setIsLoading(false);
        } catch (error) {
            console.error('Error fetching course data:', error);
            setIsLoading(false);
        }
    };

    fetchData();
}, [courseId, navigate]);

3. 子组件修改数据后触发父组件重新拉取

给TitleForm和DescriptionForm传递回调函数,当子组件完成后端数据更新后,调用该回调让父组件重新拉取最新数据:

父组件传递回调:

{!isLoading && <>
    <TitleForm 
        course={course} 
        courseId={courseId} 
        onCourseUpdate={() => fetchData()}
    />
    <DescriptionForm 
        course={course} 
        courseId={courseId} 
        onCourseUpdate={() => fetchData()}
    />
</>}

子组件示例(以TitleForm为例):

const TitleForm = ({ course, courseId, onCourseUpdate }) => {
    const [newTitle, setNewTitle] = useState(course?.title || '');

    const handleSubmit = async (e) => {
        e.preventDefault();
        // 提交数据到后端
        const response = await fetch(`http://localhost:3000/instructor/courses/${courseId}/title`, {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json',
                authorization: `Bearer ${localStorage.getItem('token')}`
            },
            body: JSON.stringify({ title: newTitle })
        });

        if (response.ok) {
            onCourseUpdate(); // 触发父组件重新拉取数据
        }
    };

    return (
        <form onSubmit={handleSubmit}>
            <input 
                type="text" 
                value={newTitle} 
                onChange={(e) => setNewTitle(e.target.value)}
            />
            <button type="submit">Save Title</button>
        </form>
    );
};

额外优化建议

  • 可给fetchData添加防抖处理,避免短时间内多次触发请求(适用于频繁更新场景)。
  • 错误处理可增加用户可见提示(如弹窗、Toast),提升交互体验。

内容的提问来源于stack exchange,提问作者Prateek Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 01:07:32