semen_nova
semen_nova

Reputation: 43

How do I trigger confirmation modal only if state changed?

I have a huge state in my component:

     language: 'rus',
     showFile: true,
     showStopButton: false,
     showPeretojka: false, 
     data: {
       id: '',
       numberLot: '',
       nameLot: '',
       tradeKind: '',
       dateTechStart: '', 
       deliveryTime: '',
       organizerId: '',
       organizerName: '',
       keyNameTrade: '',
       price: '',
       position: [],
       participant: [],
       participantConditions: [],
       afterPayFromServer: '', 
     }
   }

And whenever somthing changes in this.state.data - I want to trigger a confirmation modal. But if a user just opens a component and closes it, not changing anythig - then the component closes without triggering the confirmation modal.

I tried to use componentDidUpdate, I deep copied the this.state.data and prevState to compare them but it behaved like those two are completely equal even if I changed somthing.

For instance

componentDidUpdate(prevState, prevProps) {
   let prev = JSON.stringify(prevProps.data)
   let curr = JSON.stringify(this.state.data)

   if(curr !== prev){
     console.log('in if', prev);
   } else {
     console.log('in else', prev === curr);

   }
 }

Is there any common solution to my proplem? How does componentDidUpdate look like when one wants to trigger confirmation modal on a condition like I described above?

Upvotes: 1

Views: 270

Answers (1)

tao
tao

Reputation: 90312

The problem with what you tried is that you're expecting JSON.stringify to also sort the keys of your objects, and it does not. Basically,

const o1 = { a: null, b: null }
const o2 = { b: null, a: null }
console.log(JSON.stringify(o1) == JSON.stringify(o2)) // => false

Also, you can't use equality on objects (or arrays, dates, buffers and maps). It checks if they are the same instance, not if they have the same keys and equal values assigned to those keys).

console.log({} == {}) // => false
console.log([] == []) // => false

What you could do:

  • write your own recursive equality function which walks through the object tree and compares all key/value pairs (and when running into objects or arrays as values calls itself with those values)
  • use lodash's isEqual (which does precisely the above):
import { isEqual } from "lodash-es"

console.log(isEqual(prevProps.data, this.state.data))

Note: importing from lodash-es means you only include isEqual inside your app, not the entirety of lodash.
If you use typescript, you'll probably need to add @types/lodash-es to devDependencies.

Example:

const { isEqual } = _
const o1 = {a: null, b: null}
const o2 = {b: null, a: null}
console.log({
  "[] == []": [] == [],
  "{} == {}": {} == {},
  "isEqual(o1, o2)": isEqual(o1, o2),
  "isEqual([], [])": isEqual([], []),
  "isEqual({}, {})": isEqual({}, {}),
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

Upvotes: 1

Related Questions