React项目CI构建失败:useEffect依赖缺失导致编译错误
解决React Hook useEffect依赖缺失导致的构建失败问题
问题背景
功能逻辑:周一至周五的复选框被点击时,将所有复选框状态通过postWorkdayAnswer()接口发送至后端。
本地开发时执行npm start前端运行正常,但执行npm run build触发以下ESLint错误,导致CI流水线构建失败:
src/questions/QuestionComponent.tsx Line 22:8: React Hook useEffect存在缺失依赖:'setWorkdayAnswer'。请添加该依赖或移除依赖数组 react-hooks/exhaustive-deps
尝试过的无效方案
曾尝试将setWorkdayAnswer或setWorkdays添加为该useEffect的依赖项,但均引发日期保存异常问题。
有效解决方案
通过在父组件中使用useCallback优化回调函数,配合子组件调整useEffect依赖数组,解决了依赖缺失问题同时避免了状态异常。
修复后的父组件QuestionList.tsx
import {NavLink} from "react-router-dom"; import {useEffect, useState, useCallback} from "react"; import {getQuestionList, getTimeAnswer} from "../service/apiService"; import QuestionListComponent from "./QuestionComponent"; import {Question, TimeAnswer} from "../service/models"; import "./Questions.css" export default function QuestionList() { const [questionList, setQuestionList] = useState<Array<Question>>([]) const [errorMessage, setErrorMessage] = useState("") const [answers, setAnswers] = useState<Array<TimeAnswer>>([]) useEffect(() => { getQuestionList() .then(data => setQuestionList(data)) .catch(() => setErrorMessage("questionList doesnt load")); }, []) const onAnswer = useCallback(() => { getTimeAnswer() .then(data => setAnswers(data)) .catch(() => setErrorMessage("timeAnswer doesnt load")); }, []); const questions = questionList.sort((s1, s2) => s1.order - s2.order).map(question => <QuestionListComponent key={question.id} question={question} answers={answers} answerCallback={onAnswer}/>) return ( <div className="body"> <div className="questions"> <h1 className="questionHeadline"> Questions:</h1> {errorMessage && <div>{errorMessage}</div>} {questions} <br/> <br/> <NavLink to="/timetable"> <button className="createButton">create</button> </NavLink> </div> </div> ) }
修复后的子组件QuestionComponent.tsx
import {Question, TimeAnswer, TimeUnit, WorkdayAnswer} from "../service/models"; import {useEffect, useState} from "react"; import {getTimeUnitList, postTimeAnswer, postWorkdayAnswer} from "../service/apiService"; import TimeAnswerProperties from "./TimeAnswerProperties"; import "./QuestionComponent.css" import {convertTimeUnitToMinutes} from "../utilities/Util" interface QuestionProps { question: Question answers: Array<TimeAnswer> answerCallback: () => void // 刷新页面 } export default function QuestionListComponent(props: QuestionProps) { const [timeUnitList, setTimeUnitList] = useState<Array<TimeUnit>>([]) const [currentTimeAnswer, setCurrentTimeAnswer] = useState<string>() const [workdays, setWorkdays] = useState<boolean[]>([true, true, true, true, true, false, false]); const [errorMessage, setErrorMessage] = useState("") const question = props.question const answerCallback = props.answerCallback useEffect(() => { const workdayAnswerDbUpdate = () => { const workdayAnswer: WorkdayAnswer = { questionId: question.id, question: question.question, monday: workdays[0], tuesday: workdays[1], wednesday: workdays[2], thursday: workdays[3], friday: workdays[4], saturday: workdays[5], sunday: workdays[6], }; postWorkdayAnswer(workdayAnswer) .then(() => answerCallback()) // 刷新页面 .catch(() => { console.error("Error posting workday answer:", Error); setErrorMessage("error posting answer"); }); }; workdayAnswerDbUpdate(); }, [answerCallback, question, workdays]); useEffect(() => { const loadTimeUnitList = async () => { try { const data = await getTimeUnitList(); setTimeUnitList(data); } catch { setErrorMessage("Failed to load time unit list") } }; loadTimeUnitList(); const currentAnswer = props.answers.find(answer => answer.questionId === props.question.id) setCurrentTimeAnswer(currentAnswer ? currentAnswer.time : "00:00"); }, [props.answers, props.question.id]) const timeAnswerDbUpdate = (timeInMinutes: number) => { const timeAnswer: TimeAnswer = { questionId: props.question.id, question: props.question.question, timeInMinutes: timeInMinutes } postTimeAnswer(timeAnswer) .then(() => props.answerCallback()) // 刷新页面 .catch(() => { console.error("Error posting time answer:", Error); setErrorMessage("error posting answer"); }) }; const timeUnitsToChoose = timeUnitList .filter(timeUnit => { if (!props.question.previousQuestionId) { return true; } else { const previousQuestionAnswer = props.answers.find(answer => answer.questionId === props.question.previousQuestionId) if (previousQuestionAnswer) { const currentTimeAnswerInMinutes = convertTimeUnitToMinutes(currentTimeAnswer); return ( timeUnit.timeInMinutes! >= previousQuestionAnswer.timeInMinutes) && timeUnit.timeInMinutes !== currentTimeAnswerInMinutes; } else { return true; } } }) .map(timeUnit => <TimeAnswerProperties key={timeUnit.id} timeUnit={timeUnit}/>) const renderWorkdayCheckboxes = () => { const days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]; return days.map((day, index) => ( <span key={index}> <input type="checkbox" id={`check${index}`} checked={workdays[index]} onChange={event => { const newWorkdays = [...workdays]; newWorkdays[index] = event.target.checked; setWorkdays(newWorkdays); }} /> <label htmlFor={`check${index}`}>{day}</label> </span> )); }; const QuestionType = () => { if (props.question.question === "On which days do you work ?") { return ( <div> {renderWorkdayCheckboxes()} </div> ); } else{ return( <div> <select className="questionAnswer" name={props.question.type} id={props.question.id} onChange={event => timeAnswerDbUpdate(Number(event.target.value))} > value=<TimeAnswerProperties key={currentTimeAnswer} timeUnit={{ time: currentTimeAnswer + "", length: 15, end: "24:00" }} /> {timeUnitsToChoose} </select> </div> ); } }; return ( <div className="question"> <p>{props.question.question}</p> {errorMessage && <div>{errorMessage}</div>} {QuestionType()} </div> ); }
内容的提问来源于stack exchange,提问作者Gregor499
相关产品推荐
相关产品推荐

