tearing my hair out here so would absolutely love some help.
I'm using ReactJS to make a language learning app, and have a parent component with to host a series of questions. The questions themselves are all contained in a child component called TestComponents, and when users get the a question right it should increment a state counter called index
by one, then render the questions with this: {questions[index]}
But two of the five question components I've made are incrementing the count by two instead of one.
Each of the question components has this prop passed to it:
handleCorrect={() => handleCorrect()}
Which should call this function:
const handleCorrect = () => {
setCorrect(true);
setIndex(prevIndex => prevIndex 1);
setTimeout(() => {
setCorrect(false);
}, 2000);
};
So I can't see anywhere in there which should be causing that function to be called twice. And if I put a console.log
in it, that only appears once in my console - not twice, so it seems like this is incrementing by two instead of by one, rather than just calling the function twice.
As I say, three of the question components work fine, but the two that are breaking it are the below - apologies, because they're both quite big, but I've scoured them to try and work out what is breaking it and just cannot spot it, so I'm worried that if I only put what seem to me like the relevant parts of them, I'm worried I'll miss the part that's actually breaking it!
I have, though, put a comment saying where I am intending for it to call that function in to highlight them.
The first is this:
export const Leniter = ({
sentence,
correctSentence,
handleCorrect,
handleSubmit,
header,
topic,
}) => {
const [sentenceSplit, setSentenceSplit] = useState(sentence.split(" "));
const [submitted, setSubmitted] = useState(false);
const handleSubmitCheck = () => {
setSubmitted(true);
};
const handleClick = (word, index) => {
setSubmitted(false);
if (word.slice(1, 2) == "h") {
let sentence2 = [...sentenceSplit];
let word2 = word.slice(0, 1) word.slice(2);
sentence2[index] = word2;
setSentenceSplit(sentence2);
} else {
let sentence2 = [...sentenceSplit];
let word2 = word.slice(0, 1) "h" word.slice(1);
sentence2[index] = word2;
setSentenceSplit(sentence2);
}
};
//********Call the handleCorrect prop here*********
//handleCorrect / incorrect on submit
if ((!handleSubmit || (handleSubmit && submitted)) &&
sentenceSplit.join(" ") == correctSentence)
{
handleCorrect(topic);
setSubmitted(false)
setSentenceSplit(sentence.split(" "))
}
handleSubmit &&
submitted &&
sentenceSplit.join(" ") !== correctSentence &&
console.log("incorrect");
return (
<div className="testComponent">
{header && header == "default" ? (
<h3>
Add or remove the <i>h</i> to make the correct form of the words:
</h3>
) : (
header && <h3>{header}</h3>
)}
<Paper
sx={{
display: "flex",
justifyContent: "center",
flexWrap: "wrap",
listStyle: "none",
minHeight: "20px",
backgroundColor:
(!handleSubmit || (handleSubmit && submitted)) &&
sentenceSplit.join(" ") == correctSentence
? "lightgreen"
: handleSubmit &&
submitted &&
sentenceSplit.join(" ") !== correctSentence
? "red"
: "aliceblue",
p: 0.5,
m: 0,
borderRadius: "5px 5px 5px 5px",
border: "1px solid grey",
}}
>
{sentenceSplit.map((word, index) => {
return (
<Chip
onClick={(word) => handleClick(word.target.textContent, index)}
label={word}
sx={{ marginLeft: "5px" }}
/>
);
})}
</Paper>
{handleSubmit && (
<SubmitBtn variant="contained" onClick={() => handleSubmitCheck()}>
Submit
</SubmitBtn>
)}
</div>
);
};
And the second is this:
export const AccentSelector = ({
sentence,
handleCorrect,
handleSubmit,
header,
topic,
}) => {
const [submitted, setSubmitted] = useState(false);
let accents = "aàeèiìoòuùAÀEÈIÌOÒUÙ";
const [selectValue, setSelectValue] = useState([{}]);
const handleChange = (event, index) => {
setSubmitted(false);
setCorrect([]);
setSelectValue([
...selectValue,
{ index: index, value: event.target.value },
]);
};
//handleCorrect/incorrect
const [correct, setCorrect] = useState([]);
const handleSubmitCheck = () => {
setCorrect([]);
setSubmitted(true);
};
console.log(submitted, correct)
//********Call the handleCorrect prop here*********
if (
submitted &&
correct.length > 0 &&
correct.includes("true") &&
!correct.includes("false")
) {
handleCorrect();
setSelectValue([{}]);
setCorrect([])
}
return (
<div className="testComponent">
{header && header == "default" ? (
<h3>Select the correct versions of the vowels:</h3>
) : (
header && <h3>{header}</h3>
)}
<h3>
{sentence.split("").map((letter, index) => {
if (accents.split("").includes(letter)) {
let val =
selectValue.filter((arrayVal) => arrayVal.index == index).length >
0
? selectValue.filter((arrayVal) => arrayVal.index == index)[
selectValue.filter((arrayVal) => arrayVal.index == index)
.length - 1
].value
: "?";
let bgColor =
(!handleSubmit || (handleSubmit && submitted)) &&
selectValue.filter((arrayVal) => arrayVal.index == index).length >
0 &&
selectValue.filter((arrayVal) => arrayVal.index == index)[
selectValue.filter((arrayVal) => arrayVal.index == index)
.length - 1
].value == letter
? "lightgreen"
: (!handleSubmit || (handleSubmit && submitted)) &&
selectValue.filter((arrayVal) => arrayVal.index == index)
.length > 0 &&
selectValue.filter((arrayVal) => arrayVal.index == index)[
selectValue.filter((arrayVal) => arrayVal.index == index)
.length - 1
].value != "?"
? "red"
: "aliceblue";
//push to Correct array
if (bgColor == "lightgreen" && !correct.includes("true")) {
setCorrect([...correct, "true"]);
}
if (
(bgColor == "red" || bgColor == "aliceblue") &&
!correct.includes("false")
) {
setCorrect([...correct, "false"]);
}
switch (letter) {
case "a":
case "à":
return (
<>
<Select
onChange={(event) => handleChange(event, index)}
value={val}
autoWidth
label={index}
size="small"
style={{
backgroundColor: bgColor,
}}
>
<MenuItem value="?">?</MenuItem>
<MenuItem value="a">a</MenuItem>
<MenuItem value="à">à</MenuItem>={" "}
</Select>
</>
);
break;
case "è":
case "e":
return (
<>
<Select
onChange={(event) => handleChange(event, index)}
value={val}
autoWidth
label={index}
size="small"
style={{
backgroundColor: bgColor,
}}
>
<MenuItem value="?">?</MenuItem>
<MenuItem value="e">e</MenuItem>
<MenuItem value="è">è</MenuItem>={" "}
</Select>
</>
);
break;
default:
return <>broken</>;
}
} else {
return <>{letter}</>;
}
})}
</h3>
{handleSubmit && (
<SubmitBtn variant="contained" onClick={() => handleSubmitCheck()}>
Submit
</SubmitBtn>
)}
</div>
);
};
Thank you so much for any help!
CodePudding user response:
You should not call any logic to update state
value in the component body.
e.g. You changed state twice by setSubmitted
and setSentenceSplit
.
So handleCorrect
may be called twice because of state changes above.
if ((!handleSubmit || (handleSubmit && submitted)) &&
sentenceSplit.join(" ") == correctSentence)
{
handleCorrect(topic);
setSubmitted(false)
setSentenceSplit(sentence.split(" "))
}
You can do it in useEffect
.
useEffect(() => {
if ((!handleSubmit || (handleSubmit && submitted)) &&
sentenceSplit.join(" ") == correctSentence)
{
handleCorrect(topic);
setSubmitted(false)
setSentenceSplit(sentence.split(" "))
}
}, [submitted, handleCorrect, sentenceSplit, handleSubmit])