const [Questions, setQuestions] = React.useState(props.QuestionsData);
const handleClick = (id, isCorrect) => {
if (isCorrect) {
setQuestions((prev) => {
prev.map((item, index) => {
if (index === id) {
return {
...item,
[item.Answers.correct.selected]: !item.Answers.correct.selected,
};
} else {
return item;
}
});
});
} else {
return;
}
};
This is the code im doing and what I want to do it loop over the Questions (which has a list of objects that look like this.
{
"category": "Entertainment: Video Games",
"type": "boolean",
"difficulty": "hard",
"question": "The first "Metal Gear" game was released for the PlayStation 1.",
"correct_answer": "False",
"incorrect_answers": [
"True"
],
"Answers": {
"correct": {
"MasterId": "MXTOfKnKw7dU7QP0UP0td",
"id": 0,
"answer": "False",
"selected": false,
"correct": true,
"userChosen": true
},
"wrong": {
"id": 1,
"answer": "True",
"selected": false
}
}
}
I tried to do everything but it returns undefined. I even tried to do the map alone with the value and logged out the needed value and it worked.
If someone can tell me a good way to loop over usestate
array of objects and only edit the objects that satisfy the condition that would be great.
CodePudding user response:
Remove the curly braces so that the value is actually returned.
setQuestions((prev) =>
prev.map((item, index) => {
if (index === id) {
return {
...item,
[item.Answers.correct.selected]: !item.Answers.correct.selected,
};
} else {
return item;
}
});
);
CodePudding user response:
- I don't recommend using the capitalization convention for variable names. Use camelCase and save those for Components, Classes, and Types.
- For the sake of readability and clean code, I recommend you write data processing code outside of
setSomeState()
Here's an example.
import { useState } from "react";
...
const [questionList, setQuestionList] = useState(props.questionsData);
const handleClick = (id, isCorrect) => {
if (isCorrect) {
const updatedList = questionList.map((item, index) => {
if (index === id) {
return {
...item,
[item.answers.correct.selected]: !item.answers.correct.selected,
};
} else {
return item;
}
});
// no messy prev state stuff, it's now clean and easy to maintain
setQuestionList(updatedList);
} else {
return;
}
};
Further, I see that you use props.questionData
as a default value of the setState. It's not a good approach since one component's state's default value depends on the other component's value and can potentially cause errors.
I personally recommend you to keep the questions
default state to an empty array, and use useEffect
and do setQuestions
inside the useEffect
with props.questionData
.
CodePudding user response:
Can you imagine writing this handler every time you have array-based stated? Also map
doesn't make sense because we only want to update one item, however map
iterates over the entire array.
Write a generic function to update
an arr
at a specific index
using a user-supplied func
-
function update(arr, index, func) {
return [
...arr.slice(0, index),
func(arr[index]),
...arr.slice(index 1)
]
}
Now in your component -
function MyComponent({ questions = [] }) {
const [questions, setQuestions] = useState(questions)
const updateQuestion = index => event =>
setQuestions(arr => update(arr, index, q => ({
/* update q here */
})))
return questions.map((q, index) =>
<Question key={index} question={q} onClick={updateQuestion(index)} />
)
}
Developing your own utility functions to operate on arrays and objects can be a fun exercise, but maybe challenging to get right. Look to popular libraries like Immutable.js and Immer for inspiration.
See these Q&A for live examples you can run in your browser -
- Add Numbers in ReactJS without button
- How to change Active when it is clicked
- How to remove an element from an array passing the key using hooks?
- how to add class and remove class from buttons in react?
CodePudding user response:
You forgot to return the result of the map. If a function doesn't return anything in JS, undefined
will be returned. In addition, the computed property isn't used correctly here:
{
...item,
[item.Answers.correct.selected]: !item.Answers.correct.selected,
}
Try this:
const handleClick = (id, isCorrect) => {
if (isCorrect) {
setQuestions((prev) =>
prev.map((item, index) => {
if (index === id) {
item.Answers.correct.selected = !item.Answers.correct.selected
}
return item
}));
}
};