Home » Reactjs » React: How much can I manipulate the DOM React has rendered?

React: How much can I manipulate the DOM React has rendered?

Posted by: admin November 30, 2017 Leave a comment

Questions:

This is what I am doing: http://jsfiddle.net/iamnoah/pTrrw/

The critical section is:

position: function() {
  var container = $(this.getDOMNode());
  this._menu = $(this.refs.menu.getDOMNode());
  this._menu.appendTo(document.body).
      offset({
          top: container.offset().top + 
              container.outerHeight(),
          left: container.offset().left
      });
},
restore: function() {
  this._menu.appendTo(this.getDOMNode());      
},
componentWillUpdate: function() {
  this.restore();
},
componentDidUpdate: function() {
  this.position();
},
componentDidMount: function() {
  this.position();
},

This works great right now. I put the content back before the component updates on the assumption that React leaves the DOM alone between updates and wont miss it. In fact, React seems to be fine with moving content (if I remove componentWillUpdate and componentDidUpdate, the positioned element still updates!)

My question is how many of the resulting assumptions are safe (i.e., if I assume these things, will my code break in a future version of React?):

  • React does not care if DOM is moved around between updates as long as you put it back in componentWillUpdate.
  • React event handlers will still work on elements that have been moved.
  • React will merge any inline styles you ask it with styles already on the element (even if it did not set them.)
  • React will update DOM it has rendered, even if you move that DOM somewhere else in the document!

The last one seems somewhat extreme and magical to me, but has some great implications if it holds.

Answers:

I’m a React dev. I’ll answer each of your questions:


React does not care if DOM is moved around between updates as long as you put it back in componentWillUpdate.

True — React doesn’t look at the DOM except when updating, with the exception of event handlers:

React event handlers will still work on elements that have been moved.

I wouldn’t rely on this, and I’m not sure that it’s even true now.

React will merge any inline styles you ask it with styles already on the element (even if it did not set them.)

I also wouldn’t rely on this — right now React sets individual properties but I could easily imagine it setting el.style.cssText if that were faster, which would blow away individual changes that you’ve made.

React will update DOM it has rendered, even if you move that DOM somewhere else in the document!

I don’t believe this is true currently and you also shouldn’t rely on this.


Broadly speaking, you shouldn’t manipulate by hand the DOM that React has created. It’s 100% kosher to create an empty <div> in React and populate it by hand; it’s even okay to modify the properties of a React-rendered element as long as you don’t later try to change its properties in React (causing React to perform DOM updates), but if you move an element, React may look for it and get confused when it can’t find it.

Hope that helps.

Questions:
Answers:

On the one hand I agree with Sophie that you should not base decisions on implementation details. However, I would say it is still interesting to see how React actually works:

Some things may have changed a little bit, but generally this is what happes and how it happens.

  1. Consider the fact that React puts unique ids (now data- attributes) on every DOM element it creates. These IDs are currently based on a very simple system. This should explain the system – The root node is always ‘.0’

               .0
        .0.0        .0.1
    .0.0.0     .0.1.0 .0.1.1
    

Whenever you provide a ‘key’ attribute the value you provide is used for the id rather than it’s index in a list, but the system remains the same.

  1. The Virtual DOM created by React also has lightweight objects with the same ids. This is the simple mapping system between the virtual DOM and the real DOM.

  2. This is also why the elements in a list are overwritten if you don’t provide the ‘key’ attribute as the id of the elements would change if an element is added or removed in the middle of the list.

Keeping that in mind:

React does not care if DOM is moved around between updates as long as you put it back in componentWillUpdate.

Yes. I think it’s possible that things will work even if you don’t put the element back before updates, since React works using ids. But that would be a very tricky and unreliable to work with as elements may be added or removed and things may break.

React event handlers will still work on elements that have been moved.

Yes… to an extent. The way the event handlers in react work is that all events are synthetic. This means that there is only ever one event listener at the React root DOM node. All events caught there are matched against the event listeners in React. I think this also uses IDs to match elements. However, React may do some minimal checking of it’s parent elements. I think this should work as long as the element is kept within the same root node rendered by React. That said, React could update the id system in the future and do more checking of parent nodes in the future and this may then break.

React will merge any inline styles you ask it with styles already on the element (even if it did not set them.)

This has already been answered. Works for now, may not work in the future. I don’t it will change in the short term though, as there are some animation systems that depend on this implementation detail.

React will update DOM it has rendered, even if you move that DOM somewhere else in the document!

Yes, if you keep the same DOM element with the same react-id, react will not know it has moved in the document and will just update it as if it was in the same position where it rendered it. As you already noticed it needs to be in the same React Root. This is important, as React only binds to the React root DOM node for synthetic events etc. This way React plays well with other libraries, and doesn’t need access to the document root.

Word of Caution: just because you can do somethings doesn’t mean you should. Generally speaking you should only add additional DOM nodes and add properties on DOM nodes that are not being managed by React (style : transform values are common for animations). When you do other things, understand that things may work, but usually there is a much better way to do things.


As for your problem:

So how would you solve my problem? e.g., a dropdown menu inside a scroll area getting cut off by the overflow… I’ve always moved the element to the body, but it seems that is not the React way.

I think the ideal situation here is to put the dropdown in the body to begin with. Then you can pass messages between the click event and the drop down.
Either you can use callbacks to go high enough on the virtual DOM and then update props all the way down to the dropdown to manage it’s state. OR just use a good ol’ Event System.