Guichi
Guichi

Reputation: 2343

virtual DOM and real DOM out of sync

Recently,we want to implement some animation in React ,here is what we want:When something happens (receive a message from webSocket) ,re-order a list of react component.In my case,only one position of the item in the list will be changed .So firstly,fade out the item from the original position and then fade it in to the right place. My solution is to remove and add the corresponding item DOM using JQuery because it is easy to implement animation (fade in and fade out),meanwhile I also update the underlying data structure (on which React rely to render ) correctly expecting to make the React render DOM correctly based on the state when emitChange next time.However even if the underlying data structure is correct,React do not work as expect ,the order is still messed up An interesting part is when I inspected the react plugin in Chrome,the order of react components is correct,however ,when I inspect the real DOM,the order is incorrect.Apparently,the two DOM are out of sync

Is there a way to solve it ?

In addition,I read the animation part of React add-on ,however,I suppose it is limited,when I reorder the list ,the animation do not apply .I will appreciate if there is a react way to achieve this

Upvotes: 4

Views: 2547

Answers (1)

Jeff P Chacko
Jeff P Chacko

Reputation: 5018

While using react, we should never change the DOM directly.
Using jQuery or using Document.getElementById() to manipulate the DOM is not a good practice because of how react works.
React constructs a virtual DOM (in-memory representation of the DOM) based on the render methods from your code. And when UI state changes, render is called again and a new virtual DOM is created which is compared with the previous version of the virtual DOM and only the differences are then modified in the actual DOM. The smallest number of steps required to convert old DOM into new one are calculated and implemented. Modifying the real DOM is expensive.

https://facebook.github.io/react/docs/reconciliation.html#motivation

So when you are using jquery to change the DOM in addition to react, React would get confused because the real DOM is different from what it is expecting it to be. Try to only modify the DOM via react (i.e the render method) and this error won't pop up.

So if you really have to touch individual elements directly, you could use refs. https://facebook.github.io/react/docs/more-about-refs.html#the-ref-string-attribute

Also additionally since you are re-ordering lists, you should keep in mind some things about how react works. React adopts a very naive approach when it comes to lists. It goes over both lists at the same time and generates a mutation whenever there's a difference. So inserting an element at the top will result in react doing n number of mutations. where n is the number of elements in the list.

This is solved by adding an additional attribute called keys to the elements. Keys of the elements in the list have to be unique. React will know to compare the element only with the same keys and not mutate. It will reorder. https://facebook.github.io/react/docs/reconciliation.html#problematic-case

And instead of removing the element from DOM and reinserting, you could also set its visibility to hidden via CSS. display:none via react itself and change it back after reordering.
Or use css animations from within react triggered by a state change.
Or use find some react animation library.
Basically find a way to modify DOM only using react :)

Upvotes: 7

Related Questions