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等数据,但全局状态更新是异步的:
- 若用户直接点击收藏按钮而非先点击卡片,
selectedJobID会保留旧值; - 快速连续点击不同卡片的收藏按钮时,状态还未更新到最新就触发请求,导致提交旧数据。
修复方案
直接将当前卡片的jobInfo作为参数传递给收藏函数,不再依赖全局状态:
- 修改收藏按钮的点击事件,传递当前职位数据:
<button onClick={() => handleFavorites(jobInfo)} className='bookmark-btn' ><BsBookmark /></button>
- 更新收藏事件处理函数,直接从参数中提取数据:
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); } };
- 可选:若
handleSelectedIndex用于其他功能(如职位详情展示),可保留该逻辑,但收藏功能不再依赖它,避免状态不同步问题。
内容的提问来源于stack exchange,提问作者Christian Hernandez
相关产品推荐
相关产品推荐

