It's it possible to get React to move an element rather than re-create it when it changes its place in the DOM?
<p>It's it possible to get React to move an element rather than re-create it when it changes its place in the DOM?</p>

<p>Let's imagine I'm making a 2 pane component and I want to be able to hide/unhide one pane. Let's also imagine the panes themselves are very heavy. In my case the each pane has over 2000 elements.</p>

<p>In my actual code I'm using a splitter when there are 2 panes. In order to show just one pane I need to remove the splitter and replace it with a div.</p>

<p>The code below simulates this. If there's one pane it uses a <code>div</code> to contain the pane. If there's 2 panes it uses <code>pre</code> to contain them. In my case it would be <code>div</code> with 1 pain and a <code>splitter</code> with 2.</p>

<p>So, instrumenting <code>document.createElement</code> I see that not only are the containers created but the elements inside are recreated. In other words, in my code when go from splitter->div the 2000+ element pane will get entirely recreated which is slow.</p>

<p>Is there a way to tell React effectively. "Hey, don't recreate this component, just move it?"</p>

<p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
<div class="snippet-code">
<pre class="snippet-code-js lang-js prettyprint-override"><code>class TwoPanes extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    const panes = this.renderPanes();
    if (panes.length === 2) {
      return React.createElement('pre', {className: "panes"}, panes);
    } else {
      return React.createElement('div', {className: "panes"}, panes);
    }
  }
  renderPanes() {
    return this.props.panes.map(pane =&gt; {
      return React.createElement('div', {className: "pane"}, pane);
    });
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      panes: [
        "pane one",
        "pane two",
      ],
    };
  }
  render() {
    const panes = React.createElement(TwoPanes, {panes: this.state.panes}, null);
    const button = React.createElement('button', {
      onClick: () =&gt; {
        const panes = this.state.panes.slice();
        if (panes.length === 1) {
          panes.splice(0, 0, "pane one");  // insert pane 1
        } else {
          panes.splice(0, 1);  // remove pane 1
        }
        this.setState({panes: panes});
      },
    }, "toggle pane one");
    return React.createElement('div', {className: "outer"}, [panes, button]);
  }
}

// wrap document.createElement so we can see if new elements are created
// vs reused
document.createElement = (function(oldFn) {
  let count = 0;
  let inside = false;
  return function(type, ...args) {
    if (!inside) {  // needed because SO's console wrapper calls createElement
      inside = true;  
      console.log(++count, "created:", type);
      inside = false;
    }
    return oldFn.call(this, type, ...args);
  }
}(document.createElement));
  
  
ReactDOM.render(
  React.createElement(App, {}, null),
  document.getElementById('root')        
);</code></pre>
<pre class="snippet-code-css lang-css prettyprint-override"><code>html { box-sizing: border-box; }
*, *:before, *:after { box-sizing: inherit; }
body { margin: 0; }
#root { width: 100vw; height: 100vh; }
.outer { 
  width: 100%; 
  height: 100%; 
}
.panes { 
  width: 100%; 
  height: 100%; 
  display: flex;
  flow-direction: row;
  justify-content: space-between;
}
.pane {
  flex: 1 1 auto;
  border: 1px solid black;
}
button {
  position: absolute;
  left: 10px;
  top: 30px;
}</code></pre>
<pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"&gt;&lt;/script&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"&gt;&lt;/script&gt;
&lt;div id="root"&gt;&lt;/div&gt;</code></pre>
</div>
</div>
</p>