添加Linter提示的依赖后出现React无限重渲染循环,如何解决并消除警告?
解决React useEffect依赖警告与无限循环问题
原组件代码
import { React, useState, useEffect } from "react"; import QuestionContext from "./QuestionContext"; import questions from "../data/questions.json"; import { useNavigate } from "react-router-dom"; const SectionState = (props) => { // set questions from json to an array of 4 elements const newQuestions = Object.keys(questions.content).map( (key) => questions.content[key].question ); //useState for Question state const [currentQuestion, setCurrentQuestion] = useState(0); const newQuestionsArr = { qID: 0, questionTxt: newQuestions[currentQuestion], } const [questionCtx, setQuestionCtx] = useState(newQuestionsArr); const navigate = useNavigate() useEffect(() => { setQuestionCtx(prevState => ({ ...prevState, qID: currentQuestion, questionTxt: newQuestions[currentQuestion], })); }, [currentQuestion]); const updateNextQuestion = () => { if (!(currentQuestion >= newQuestions.length)) { setCurrentQuestion((nextCurrentQuestion) => nextCurrentQuestion + 1); } else{ navigate('/result') } }; const updatePrevQuestion = () => { if (currentQuestion <= 0) { console.log(`No more questions`); } else { setCurrentQuestion((prevCurrentQuestion) => prevCurrentQuestion - 1); } }; return ( <QuestionContext.Provider value={{ questionCtx, updateNextQuestion, updatePrevQuestion }}> {props.children} </QuestionContext.Provider> ); }; export default SectionState;
Linter警告
React Hook useEffect has a missing dependency: 'newQuestions'. Either include it or remove the dependency array
问题分析
newQuestions是每次组件渲染时都会重新创建的数组,即使内容未变,引用地址也会更新。如果把它加入useEffect的依赖数组,会触发useEffect重复执行,进而更新questionCtx导致组件重渲染,形成无限循环。
解决方案
方案一:用useMemo缓存newQuestions
通过useMemo缓存数组,只有当依赖的questions发生变化时才重新生成(导入的JSON文件是静态资源,不会变化,所以newQuestions只会创建一次):
import { React, useState, useEffect, useMemo } from "react"; // ...其他导入不变 const SectionState = (props) => { // 用useMemo缓存newQuestions const newQuestions = useMemo(() => { return Object.keys(questions.content).map( (key) => questions.content[key].question ); }, [questions]); const [currentQuestion, setCurrentQuestion] = useState(0); const [questionCtx, setQuestionCtx] = useState({ qID: 0, questionTxt: newQuestions[0], }); const navigate = useNavigate() useEffect(() => { setQuestionCtx(prevState => ({ ...prevState, qID: currentQuestion, questionTxt: newQuestions[currentQuestion], })); }, [currentQuestion, newQuestions]); // 安全加入newQuestions到依赖 // ...updateNextQuestion、updatePrevQuestion和return部分不变 };
方案二:移除questionCtx状态,直接计算上下文值(更优)
questionCtx完全可以通过currentQuestion和newQuestions实时计算,不需要单独维护state,从根源消除useEffect和依赖问题:
import { React, useState, useMemo } from "react"; // ...其他导入不变 const SectionState = (props) => { const newQuestions = useMemo(() => { return Object.keys(questions.content).map( (key) => questions.content[key].question ); }, [questions]); const [currentQuestion, setCurrentQuestion] = useState(0); const navigate = useNavigate() // 直接计算questionCtx,无需state和useEffect const questionCtx = { qID: currentQuestion, questionTxt: newQuestions[currentQuestion], }; const updateNextQuestion = () => { if (!(currentQuestion >= newQuestions.length)) { setCurrentQuestion((nextCurrentQuestion) => nextCurrentQuestion + 1); } else { navigate('/result') } }; const updatePrevQuestion = () => { if (currentQuestion <= 0) { console.log(`No more questions`); } else { setCurrentQuestion((prevCurrentQuestion) => prevCurrentQuestion - 1); } }; return ( <QuestionContext.Provider value={{ questionCtx, updateNextQuestion, updatePrevQuestion }}> {props.children} </QuestionContext.Provider> ); }; export default SectionState;
说明
方案二更简洁高效,避免了额外的state更新和useEffect执行,直接通过当前状态计算上下文值,同时用useMemo确保newQuestions不会重复创建,彻底消除了linter警告和无限循环风险。
内容的提问来源于stack exchange,提问作者dartHTh0t
相关产品推荐
相关产品推荐

