I’m a little stuck on this problem, I need to identify why it happening and how we can solve it.
The issue is that I have s nested array of Questions and I’m showing up the Modal onClick when the user hits the option and the Modal shows the Sub questions in the Modal. that are nested into questions items
But when we click on the Sub Questions inside the Modal, Modal gets flickered and re-renders itself. , Maybe there’s of Component State. I’m not sure why it happens, please let me know to fix it.
Iv retracted a lot of code here. To make it simpler to read. Related Code:
// passing question from mapped array on change onChange={() => modalHandler(question)}
Now the modal part
function modalHandler(question) { // Will be used to map the sub questions in modal setCurrent(question) // .... setModalShow(true) } function SubQuestionModal(props) { function subQuestionsHandler(sub, questionId) { // some actions onChange to update state // ... newData[indexOfParent].child = childData setData(newData) localStorage.setItem('deviceReport', JSON.stringify(newData)) } return ( <Fragment> <Modal {...props} size="lg" aria-labelledby="contained-modal-title-vcenter" centered> <Modal.Body> <Row xs={3} md={3} className="g-3"> {current.sub? ( current.sub.map((sub) => ( <Col> <ToggleButton key={sub.id} onClick={(e) => subQuestionsHandler(sub, questionInUse.id)} // On this click modal gets flickered . > </ToggleButton> </Col> )) ) : null} </Row> </Modal.Body> </Modal> </Fragment> ) }
And I’m rendering modal like this
<SubQuestionModal show={modalShow} onHide={() => setModalShow(false)} />
That’s all code is in one component. Original Component Code is there, If you wanna go into more depth https://github.com/abelladona78/ModalProblem/blob/main/Inspection.js
Advertisement
Answer
There are some fundamental problems in your code:
- never mutate state directly
- split the component into smaller components
- store the minimum necessary state
Some refactoring might be required. I’ll try to explain the problems. I think if you fix these, you probably will solve your problem after a while:
1. never mutate state directly
In React you should never change the state directly. Always use the setState()
hook and set it to a new object.
See Why Immutability Is Important for further information.
React says e.g. regarding setstate():
state … should not be directly mutated. Instead, changes should be represented by building a new object based on the input from state and props.
(These links are about class-components, but the rule is the same for function-components)
You should also understand the difference between reference and value (aka. “primitive”) in javascript.
Examples:
(This is probably not the best approach, I just illustrate where the problems are)
// BAD: data[indexOfQuestion] = { question: question.if_no, parentId: question.id, userChoice: 'NO', child: [] } // BETTER: setData( data.map( (item, index) => { return index === indexOfQuestion ? { // Create new item. React knows it has changed. question: question.if_no, parentId: question.id, userChoice: 'NO', child: [] } : item; // Same object as before. React knows it is unchanged. }));
// BAD: let newData = [...theData] // <-- (ok) newData is [ oldItem, oldItem, ... }] newData[indexOfParent].child = childData // <-- (BAD) oldItem.data is changed setTheData(newData) // <-- (ok) newData still contains old references // BETTER: let newData = [...theData] newData[indexOfParent] = { ...data[indexOfParent], child: childData } // <-- New object setTheData(newData)
2. split the component into smaller components
This is not an error, but I think quite important, because if you don’t do it, your components become increasingly complex, and almost impossible to debug after a while.
Really, just try it with one or two components, I bet you will see that suddenly everything will become a bit clearer and better understandable and maintainable.
For further information see the React docs “Extracting Components”:
Don’t be afraid to split components into smaller components
3. store the minimum necessary state
Also not an error, but you should not store whole objects in the state, if you can avoid it.
That way you would never have the problems with references (as described in #1).
E.g.
// AVOID if possible: function modalHandler( question ) { setQuestionInUse( question ) // <-- don't store whole object // ... } // BETTER: function modalHandler( question ) { setQuestionInUse( question.id ) // <-- only store id // ... }
Maybe you can also reduce the data stored in the data
state. E.g. just an array of ids. But I can’t figure that out right now.
Of course you need to store the loaded data somewhere, but maybe you don’t need to modify that loaded data, but
use e.g. a separate displayedData
list.