Skip to content
Advertisement

How to stop flickering of modal on event change

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:

  1. never mutate state directly
  2. split the component into smaller components
  3. 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.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement