在我的 App.tsx 文件中,我想暂停所有状态并调用 useEffect 钩子来获取测验数据。当它第一次运行时,它运行得很好,但是当我单击 handleRetake 函数重新进行测验时,它重置了所有状态,但没有调用 useEffect 钩子来调用 API。 这是我的代码
function App() {
let [quiz,setQuiz] = useState<QuizType[]>([])
let [currentStep,setCurrentStep] = useState(0)
let [score,setScore] = useState(0)
let [showResult, setShowResult] = useState(false)
useEffect(()=>{
async function fetchData() {
const questions = await getQuizDetails(5,'easy')
setQuiz(questions)
}
fetchData();
},[])
const handleRetake = (event:React.FormEvent<EventTarget>) => {
event.preventDefault();
setCurrentStep(0)
setQuiz([])
setShowResult(false)
setScore(0)
}
if(!quiz.length)
return <Container>
<h1>Loading ..... </h1>
</Container>
if(showResult){
return(
<Container>
<Row>
<Col md={{span:6 , offset:3}}>
<Card>
<Card.Header>
Result
</Card.Header>
<Card.Body>
<h1>Your Score is {score} out of {quiz.length}</h1>
<Button onClick={handleRetake}> Retake Quiz</Button>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
)
}
答案 0 :(得分:1)
您可以做的一件事是将 quiz
添加到 useEffect
的依赖项列表并添加检查,以便每当 quiz
为空列表时,重新获取它。
useEffect(()=>{
if(quiz.length === 0) {
async function fetchData() {
const questions = await getQuizDetails(5,'easy')
setQuiz(questions)
}
fetchData();
}
},[quiz])
此外,这只是满足您要求的方法之一。实际上,您也可以在 fetchData
之外声明 useEffect
以在需要时重用它。同样使用此方法,您必须确保您的 getQuizDetails
返回一个非空列表,否则您将陷入一次又一次获取列表的循环中。
答案 1 :(得分:0)
@lakshya-thakur 在最后的回答中暗示了这一点,但我觉得这非常重要。
根据我目前所学/经验,我们应该避免在状态更新本身时触发改变状态变量值的 API/函数调用。相反,消除 API/函数调用对状态更新的依赖,而是在外部/基于用户的事件(初始页面呈现 - useEffect(()=>{},[])
和/或按钮点击)上触发它们将防止不必要的组件重新呈现并提供更好、更强大的解决方案。这也消除了 useEffect
内部不必要的检查,这些检查是决定状态是否需要更新或 API/函数调用是否也需要进行的必要检查。
在下面的代码中,initQuizData
函数在初始组件渲染时被调用,并且在点击 retake quiz
按钮时被调用。如您所见,API/函数调用 getQuizDetails
不再依赖于状态变量 quiz
的值,而是依赖于实际打算进行该 API/函数调用的外部因素。
相反,每次更新 fetchData
状态时都会调用 quiz
(我同意在这种情况下只是在测验之前和测验之后一次,但遵循相同的模式可能会结束对频繁更改的状态执行此操作),此处 initQuizData
仅在需要时才被调用,因此在其中不需要进行条件检查。
function App() {
let [quiz,setQuiz] = useState<QuizType[]>([]);
let [currentStep,setCurrentStep] = useState(0);
let [score,setScore] = useState(0);
let [showResult, setShowResult] = useState(false);
useEffect(()=>{
initQuizData();
},[]);
const initQuizData = async () => {
event.preventDefault();
setCurrentStep(0);
setQuiz([]);
setShowResult(false);
setScore(0);
const questions = await getQuizDetails(5,'easy');
setQuiz(questions);
};
if(!quiz.length)
return <Container>
<h1>Loading ..... </h1>
</Container>
if(showResult){
return(
<Container>
<Row>
<Col md={{span:6 , offset:3}}>
<Card>
<Card.Header>
Result
</Card.Header>
<Card.Body>
<h1>Your Score is {score} out of {quiz.length}</h1>
<Button onClick={initQuizData}> Retake Quiz</Button>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
);
}
上面的解决方法很粗糙,我没有亲自测试过。我只是想传达我上面所说的想法,作为处理此类情况的方法。