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

onClick回调与POST请求不同步问题求助

问题描述

开发了职位收藏功能:前端通过React按钮的onClick回调触发POST请求,将职位信息保存到Flask后端数据库;后端已实现重复收藏校验逻辑(重复时返回400状态码)。

测试发现异常:

  • 收藏已存在的职位时,系统正确返回400;
  • 后续收藏新职位时仍返回400;
  • 第三次请求返回201,但保存的是上一次点击的职位ID;
  • 控制台显示每次点击的job_id都是上一次的,存在执行延迟。

Postman测试后端功能正常,怀疑是前端问题。


Flask后端收藏接口代码

class Favorites(Resource):
    def post(self):
        request_json = request.get_json()
        job_id = request_json.get('job_id')
        user_id = request_json.get('user_id')

        # Check if the job is already favorited by the user
        existing_favorite = Favorite.query.filter_by(job_id=job_id, user_id=user_id).first()
        if existing_favorite:
            return make_response({"error": "Job already saved"}, 400)  # Use 400 for bad request
        
        # If the job is not already favorited, proceed to create the favorite entry
        new_favorite = Favorite(
            employer_logo=request_json.get('employer_logo'),
            job_title=request_json.get('job_title'),
            employer_name=request_json.get('employer_name'),
            job_city=request_json.get('job_city'),
            job_state=request_json.get('job_state'),
            job_min_salary=request_json.get('job_min_salary'),
            job_max_salary=request_json.get('job_max_salary'),
            job_employment_type=request_json.get('job_employment_type'),
            job_apply_link=request_json.get('job_apply_link'),
            job_description=request_json.get('job_description'),
            job_qualifications=request_json.get('job_qualifications'),
            job_responsibilities=request_json.get('job_responsibilities'),
            job_benefits=request_json.get('job_benefits'),
            job_id=job_id,
            user_id=user_id
        )

        try: 
            db.session.add(new_favorite)
            db.session.commit()
            return make_response(new_favorite.to_dict(), 201)  # Use 201 for created
        
        except Exception as e:
            print(e)
            return make_response({'error': 'Unprocessable Entity'}, 500)  # Use 500 for server error

    def get(self):
        # Retrieves all favorite jobs
        favorites = [favorites.to_dict() for favorites in Favorite.query.all()]
        return make_response(jsonify(favorites), 200)

api.add_resource(Favorites, '/favorites')

React前端收藏按钮组件代码

<div className='search-results'>
    <div className='search-query'>
        <h4 className='search-result-header'>
            {searchedJobData && searchedJobData.parameters
                ? searchedJobData.parameters.query
                : "Search for jobs"}
        </h4>
    </div>
    {slicedSearchedJobData?.map((jobInfo, index) => (
        <div onClick={() => handleSelectedIndex(index)} key={`${jobInfo.job_id}-${jobInfo.job_title}`} className='search-job-card'>
            <div className='search-job-card-wrapper'>
                <h4>{jobInfo.job_title}</h4>
                <p className='employer-name'>{jobInfo.employer_name}</p>
                <p>{jobInfo.job_city}, {jobInfo.job_state}</p>
                {jobInfo.job_min_salary && jobInfo.job_max_salary ? <p className='job_salary'>${jobInfo.job_min_salary} - ${jobInfo.job_max_salary}</p> : <p className='job_salary'>$N/A</p>}
            </div>
            <button onClick={handleFavorites} className='bookmark-btn' ><BsBookmark /></button>
        </div>
    ))}
    <button onClick={handleLoadMore} className='load-more-btn'>Load More</button>
</div>

React收藏事件处理函数

const handleFavorites = async () => {
    console.log("Sending POST request to favorites API...");
    const favoritesUrl = "http://127.0.0.1:5000/favorites";

    const job_id = selectedJobID;
    const user_id = user.id;
    const job_title = selectedJobTitle;
    const employer_name = selectedEmployer;
    const job_city = selectedJobCity;
    const job_state = selectedJobState;
    const job_min_salary = selectedJobMinSalary;
    const job_max_salary = selectedJobMaxSalary;
    const job_employment_type = selectedJobEmploymentType;
    const job_apply_link = selectedJobApplyLink;
    const job_description = selectedJobDescription;

    console.log(job_id)

    if (!user) {
        alert("Must be signed in to bookmark a job.");
        return;
    }

    try {
        const response = await fetch(favoritesUrl, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                job_id,
                user_id,
                job_title,
                employer_name,
                job_city,
                job_state,
                job_min_salary,
                job_max_salary,
                job_employment_type,
                job_apply_link,
                job_description
            }),
        });

        if (response.ok) {
            // Successfully added to favorites
            const newFavorite = await response.json();
            // Update the newFavoriteJobData state with the newFavorite before checking
            dispatch(setNewFavoriteJobData([...newFavoriteJobData, newFavorite]));
            console.log("Job added to favorites.");
        } else {
            // Handle the case where the server responded with an error
            console.error("Failed to add job to favorites.");
        }
    } catch (error) {
        // Handle network errors
        console.error("An error occurred while adding to favorites:", error);
    }
};

问题分析与解决方案

核心问题

收藏功能依赖全局状态selectedJobID等数据,但全局状态更新是异步的:

  1. 若用户直接点击收藏按钮而非先点击卡片,selectedJobID会保留旧值;
  2. 快速连续点击不同卡片的收藏按钮时,状态还未更新到最新就触发请求,导致提交旧数据。

修复方案

直接将当前卡片的jobInfo作为参数传递给收藏函数,不再依赖全局状态:

  1. 修改收藏按钮的点击事件,传递当前职位数据:
<button onClick={() => handleFavorites(jobInfo)} className='bookmark-btn' ><BsBookmark /></button>
  1. 更新收藏事件处理函数,直接从参数中提取数据:
const handleFavorites = async (jobInfo) => {
    console.log("Sending POST request to favorites API...");
    const favoritesUrl = "http://127.0.0.1:5000/favorites";

    // 直接从传入的jobInfo中提取数据,避免依赖全局状态
    const job_id = jobInfo.job_id;
    const user_id = user?.id;
    const job_title = jobInfo.job_title;
    const employer_name = jobInfo.employer_name;
    const job_city = jobInfo.job_city;
    const job_state = jobInfo.job_state;
    const job_min_salary = jobInfo.job_min_salary;
    const job_max_salary = jobInfo.job_max_salary;
    const job_employment_type = jobInfo.job_employment_type;
    const job_apply_link = jobInfo.job_apply_link;
    const job_description = jobInfo.job_description;

    console.log(job_id)

    if (!user) {
        alert("Must be signed in to bookmark a job.");
        return;
    }

    try {
        const response = await fetch(favoritesUrl, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                job_id,
                user_id,
                job_title,
                employer_name,
                job_city,
                job_state,
                job_min_salary,
                job_max_salary,
                job_employment_type,
                job_apply_link,
                job_description
            }),
        });

        if (response.ok) {
            const newFavorite = await response.json();
            dispatch(setNewFavoriteJobData([...newFavoriteJobData, newFavorite]));
            console.log("Job added to favorites.");
        } else {
            const errorData = await response.json();
            console.error("Failed to add job to favorites:", errorData.error);
        }
    } catch (error) {
        console.error("An error occurred while adding to favorites:", error);
    }
};
  1. 可选:若handleSelectedIndex用于其他功能(如职位详情展示),可保留该逻辑,但收藏功能不再依赖它,避免状态不同步问题。

内容的提问来源于stack exchange,提问作者Christian Hernandez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 06:12:08