I’ve seen some solutions for other frameworks but none apply to me. I have two main issues here.
1- I have to click the button twice to get it to fire. Granted I am using the click even’t to mutate the state, change the row color, AND also update a list on the bottom of the table. I don’t think none of these are running asynchronously. So maybe that’s the problem and I would like to know how to go around that.
2- Second problem is related to the first. I passed the dependency in the useEffect function so that the list on the bottom is updated as soon as the click event changes the state. But it’s throwing an infinite loop, as if the state was constantly changing. A console log doesn’t reflect the state forever changing. So, I’m stooped.
Some basically I have an array (API in real world), and I’m filtering out the learned and unlearned questions
const questions = [
{
question: "Question 1",
answer: "My answer 1",
learned: true
},
{
question: "Question 2",
answer: "My answer 2",
learned: false
},
{
question: "Question 3",
answer: "My answer 3",
learned: true
},
{
question: "Question 4",
answer: "My answer 4",
learned: true
},
{
question: "Question 5",
answer: "My answer 5",
learned: false
},
{
question: "Question 6",
answer: "My answer 6",
learned: true
}
]
const learned = questions.filter(question => question.learned === true)
const unlearned = questions.filter(question => question.learned === false)
Inside my component I have the state, the function to populate a list, useEffect, and a click event function that contains the callback to populate the list, as well as toggling the UI from learned to unlearned, which of course, it updates the list accordingly
const [unlearned, setUnlearned] = useState([]);
const populateUnlearned = () => {
setUnlearned(questions.filter(question => question.learned === false))
}
useEffect(() => {
populateUnlearned()
},[]) //unlearned here causes the list to populate fine but an infinite loop
const handleLearned = (index) => {
populateUnlearned()
setIsLearned(
!isLearned,
questions.filter(question => question.learned === false),
questions[index].learned = !isLearned
)
}
And the rest is just html stuff. I have a sandbox here for you to mess with showing the problem.
I appreciate any help. Thanks in advance
2
Answers
Use
instead of
You can’t use single boolean to hold the current state of
learned
on each row. You don’t even need a separate state for that, you are already holding it inquestions[index].learned
, you just have to invert itIt is not the
React
way of doing so, but it will work, you just have to be sure that there is a rerender after setting the new value toquestions[index].learned
And setIsLearned does not accept that many parameters, it accepts only 1 to change its value
And not everything have to be state. You don’t need
unlearned
state as well. You already have the arrayquestions
, you can filter it while rendering. If it is an expensive operation, instead of moving it to a separate state, you can useuseMemo
You can remove
isLearned
andunlearned
states and movequestions
to its own stateThis is just enough, no need of state
here are a few updates I would recommend,
In Vocabulary.js, you attempt to modify the entries in
questions
. However, this variable is not a piece of state. Keep in mind that only variables created based onuseState
(anduseReducer
) can be updated within your componenthere you can find information that explains in detail why you should not be using the index