我將盡我所能詳細說明我正在開發的應用程式以及我現在面臨的問題,首先,感謝您抽出寶貴時間閱讀并幫助我!我目前正在開發一個語言評估應用程式,為此,我使用 react js 和 Firestore 來存盤問題。我基本上是在獲取問題,將它們存盤在父組件狀態并將問題傳遞給 Question 組件。一旦用戶按下下一個按鈕,我在會話存盤上就有一個遞增的計數器,我還跟蹤用戶在會話存盤上的進度,并訪問一個不同的問題,使組件重新呈現。
這按預期完美運行,但是,我現在遇到的問題是整個問題頁面正在重新呈現,我在頁面頂部有一個進度條組件,每次出現問題時都不應該重新呈現變化,因為他的父母狀態沒有改變。
這是我的代碼:
TestQuestionsPage 組件代碼 - 父級
import { useEffect } from 'react';
import { ProgressBar } from '../../ProgressBar/ProgressBar';
import { QUESTION_TYPES } from '../../../utils/constants';
import QuestionStateHandler from '../../QuestionStateHandler/QuestionStateHandler';
export default function TestQuestionsPage() {
useEffect(() => {
console.log('re rendering');
return () => console.log('testQuestionPage unmounting');
}, []);
return (
<div>
<ProgressBar questionsType={QUESTION_TYPES.GRAMMAR} />
<QuestionStateHandler />
</div>
);
}
QuestionStateHandler 組件 - 管理每個問題的狀態更改的組件
import React, { useEffect, useState } from 'react';
import Question from '../Question/Question';
import { queryQuestions } from '../../utils/firebase-utils';
import { encryptData } from '../../utils/crypto-utils';
export default function QuestionStateHandler() {
const [testType, setTestType] = useState('grammarQuestions');
const [level, setLevel] = useState('A1');
//max questions will change based on the testType;
const [maxQuestionsPerLevel, setMaxQuestionsPerLevel] = useState(10);
const [setOfQuestions, setQuestions] = useState(null);
//state variable that will hold the user score
const [userScore, setUserScore] = useState(0);
//total number of questions
const [totalQuestions, setTotalQuestions] = useState(50);
useEffect(() => {
//if we don't have any questions
console.log('fetching questions');
queryQuestions(
testType,
['A1', 'A2', 'B1', 'B2', 'C1'],
maxQuestionsPerLevel,
).then((res) => {
console.log(res);
const encryptedQuestions = encryptData(res);
setTotalQuestions(res.length);
setQuestions(encryptedQuestions);
});
return () => console.log('unmounting component');
}, [testType]);
return (
<Question
totalQuestions={totalQuestions}
testType={testType}
setTestType={setTestType}
setOfQuestions={setOfQuestions && setOfQuestions}
/>
);
}
問題組件
import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useSessionStorage } from '../hooks/useSessionStorage';
import { QUESTION_TYPES } from '../../utils/constants';
import MediumButton from '../MediumButton/MediumButton';
import QuestionAttachment from '../QuestionAttachment/QuestionAttachment';
import './Question.css';
import {
decryptQuestion,
encryptData,
decryptData,
} from '../../utils/crypto-utils';
export default function Question({
totalQuestions,
testType,
setTestType,
setOfQuestions,
}) {
const [checkedOption, setCheckedOption] = useState(null);
const [isOptionSelected, setIsOptionSelected] = useState(false);
const [questionObj, setQuestionObj] = useState(null);
const [questionID, setQuestionID] = useState(null);
const [correctAnswer, setCorrectAnswer] = useState(null);
const [counter, setCounter] = useSessionStorage('counter', 0);
const [isLastQuestion, setLastQuestion] = useState(false);
// const { totalQuestions, testType, setTestType } = useAppContext();
const navigate = useNavigate();
// this function will be used to create the user progress object and track their correct answers,
function testTakerProgress(qID, isTheAnswerCorrect) {
//create a new object and encrypt it
const userProgressObj = {
question_level: questionObj.level,
question_id: questionID,
has_answered_correctly: isTheAnswerCorrect,
};
const userProgress = sessionStorage.getItem('user');
//if we have an user progress object already created
if (userProgress) {
let currentProgress = decryptData(userProgress);
currentProgress = [userProgressObj, ...currentProgress];
sessionStorage.setItem('user', encryptData(currentProgress));
console.log(currentProgress);
} else {
//we don't have an user progress created
const progressArray = [];
progressArray.push(userProgressObj);
sessionStorage.setItem('user', encryptData(progressArray));
console.log(progressArray);
}
}
useEffect(() => {
if (setOfQuestions) {
const q = decryptQuestion(counter, setOfQuestions);
console.log(q);
const qID = Object.keys(q);
setQuestionID(...qID);
setQuestionObj(q[qID]);
console.log(totalQuestions);
}
return () => {
setQuestionObj(null);
setQuestionID(null);
};
}, [setOfQuestions]);
useEffect(() => {
if (isLastQuestion === true) {
console.log('we are at the last question');
}
}, [isLastQuestion]);
function handleNext() {
//incrementing the question counter
setCounter((prevCount) => parseInt(prevCount) 1);
if (checkedOption === correctAnswer) {
testTakerProgress(questionID, true);
} else {
testTakerProgress(questionID, false);
}
if (counter === totalQuestions - 1) {
setLastQuestion(true);
}
}
function handleSubmit() {
console.log('unmounting the question component');
//navigate to the test page, unmount the component
navigate('/');
}
return (
questionObj && (
<div className="pageContainer">
{testType === QUESTION_TYPES.LISTENING && (
<div
className={`${
testType === QUESTION_TYPES.GRAMMAR && 'disabled'
} questionAttachmentContainer`}
>
<QuestionAttachment
questionType={testType}
questionAttachmentTitle="title"
questionAttachmentBody={questionObj.mediaURL}
/>
</div>
)}
<div className="questionContainer">
<h4 className="questionInstruction">{questionObj.question}</h4>
{/* <p className="questionPrompt">{questionPrompt}</p> */}
<form className="formContainer">
{questionObj.options &&
questionObj.options.map((option, index) => (
<div
className={`optionContainer ${
checkedOption === index && 'activeLabel'
}`}
key={index}
>
<input
id={index}
type="radio"
checked={checkedOption === index}
onChange={() => {
setIsOptionSelected(true);
console.log(isOptionSelected);
setCheckedOption(index);
}}
/>
<label htmlFor={index}>{option}</label>
</div>
))}
<div className="buttonContainer">
<MediumButton
text={isLastQuestion ? 'Submit' : 'Next'}
onClick={isLastQuestion ? handleSubmit : handleNext}
disabled={isOptionSelected ? '' : 'disabled'}
/>
</div>
</form>
</div>
</div>
)
);
}
感謝你的寶貴時間!!我還附上了一個 gif 來向您展示這個錯誤。
應用程式 Gif
我嘗試了幾件事和不同的方法,但到目前為止似乎沒有任何效果。
uj5u.com熱心網友回復:
在我看來,從 gif 開始,整個頁面都在重新加載,但我不確定 MediumButton 組件內部是什么。默認情況下,表單元素內的按鈕將在單擊時提交表單,您需要在按鈕 onClick 處理程式中實作 preventDefault,這是一個示例:
function App() {
const handleClick = (e) => {
e.preventDefault();
}
return (
<div className="App">
<form>
<p>This is simple form</p>
<button onClick={(e) => handleClick(e)}>hello</button>
</form>
</div>
)
};
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/450520.html
