title: React: How to Animate Expanding and Collapsing Div When the Size of the Content is Not Knowable
body: <h1>Question:</h1>
  <p>I have a React functional component that recursively renders nested lists.  That part is working fine.</p>
  <p>The part that I am struggling with is getting the divs that contain the nested lists to have an expanding animation when the nested lists (of varying sizes) appear and disappear.</p>
  <p>Because the amount of content is not known, simply animating the max-height property does not work.   For example, when one level of lists is rendered, the height of the frame might expand to 100px.  However, if you animate the max-height to 100px, then the div can not expand later on to accommodate more and more nested lists that get expanded.</p>
  <h3>The div that needs to be animated is:</h3>
  <pre><code>&lt;div className=&quot;frame&quot;&gt; [nested ordered lists] &lt;/div&gt;
  </code></pre>
  <h3>... and the function that is not working is the function named &quot;collapseFrame.&quot;</h3>
  <p>Again, I previously tried using a CSS transition on the max-height property but that is much less than an ideal solution, because it does not work well on unpredictable heights.  So, I don't want to do it that way, if at all possible.</p>
  <p>I followed a tutorial and got it working with vanilla JavaScript, without React (parts of the code that are commented out), but when translating it to React, I am not sure why I can not get the code to work.</p>
  <p>At the moment, the expanding animation is working, but it will stop working if the border of the  is removed or changed to the color white.  I don't know why. ??</p>
  <p>**  Also, the collapsing animation is not working at all.  The 'transitioned' event does not always fire on the ref object, and sometimes fires after it is supposed to have been removed.</p>
  <p>Is anyone able to help point me in the right direction?</p>
  <h2>This is my codepen.</h2>
  <p>(I tried to transfer it to JS Fiddle, but it's not working).</p>
  <h3><a href="https://codepen.io/maiya-public/pen/ZEzoqjW" rel="nofollow noreferrer">https://codepen.io/maiya-public/pen/ZEzoqjW</a></h3>
  <p>(another attempt on codepen, for reference.  The nested lists are appearing and disappearing, but not transitioning).
  <a href="https://codepen.io/maiya-public/pen/MWgGzBE" rel="nofollow noreferrer">https://codepen.io/maiya-public/pen/MWgGzBE</a></p>
  <p>And here is the raw code:
  (I think it is easier to understand on the codepen, but pasting here for good practice).</p>
  <p>index.html</p>
  <pre><code>&lt;div id=&quot;root&quot;&gt;
  &lt;/div&gt;
  </code></pre>
  <p>style.css</p>
  <pre><code>.frame {
      overflow:hidden;
      transition: all 0.5s ease-out;
      height:auto;
  //  for some reason,without the border, not all of them will transition AND it can't be white !?? 
      border: solid purple 1px; 
  }
  
  button {
     margin: 0.25rem;
  }
  </code></pre>
  <p>Dummy data: (the nested lists will render based on this object)</p>
  <pre><code>let data = { 
   text: 'list',
    children: [        
    {
  text: &quot;groceries&quot;,
  children: [
    {
      text: &quot;sandwich&quot;,
      children: [
        {
          text: &quot;peanut butter&quot;,
          children: [{text: 'peanuts', children: [{text: 'nut family'},{text: 'plant', children: [{text: 'earth'}]}] }]
        },
        {
          text: &quot;jelly&quot;,
          children: [
            { text: &quot;strawberries&quot;, children: null },
            { text: &quot;sugar&quot;, children: null }
          ]
        }
      ]
    }
  ]
    },
      {
      text: &quot;flowers&quot;,
      children: [
        {
          text: &quot;long stems&quot;,
          children: [
            {
              text: &quot;daisies&quot;,
              children: null
            },
            {
              text: &quot;roses&quot;,
              children: [
                { text: &quot;pink&quot;, children: null },
                { text: &quot;red&quot;, children: null }
              ]
            }
          ]
        }
      ]
    }
  ] };
  </code></pre>
  <p>React code:
  index.js</p>
  <pre><code>// component recursively renders nested lists.  Every list item is a list.
  const ListItem = ({item, depth}) =&gt; {
  //   depth prop allows me to give a className depending on  how deeply it is nested, and do CSS style based on that
    let { text, children } = item
    let [showChildren, setShowChildren] = React.useState(false)
    let frame = React.useRef()
  
    const expandFrame = (frame) =&gt; {
        //let frameHeight = frame.style.height //was using this at one point, but not anymore b/c not working
        // was supposed to have frame.style.height = frameHeight + 'px'
        frame.style.height = 'auto'
        frame.addEventListener('transitionend', () =&gt;{
          frame.removeEventListener('transitionend', arguments.callee)
          frame.style.height = null
       })
    }
  
    const collapseFrame = (frame) =&gt; {
     let frameHeight = frame.scrollHeight;
  
   // temporarily disable all css transitions
    let frameTransition = frame.style.transition;
    frame.style.transition = ''
  
    requestAnimationFrame(function() {
      frame.style.height = frameHeight + 'px';
      frame.style.transition = frameTransition;
      requestAnimationFrame(function() {
        frame.style.height = 0 + 'px';
      })
      })
        }
  
    return(
  &lt;ol&gt; 
        &lt;button onClick={(e)=&gt;{
            if(!showChildren) {
                // console.log('children not showing/ expand')
               setShowChildren(true)
              expandFrame(frame.current)
            } else {
              // console.log('children showing/ collapse')
               setShowChildren(false)
               collapseFrame(frame.current)
            }
          }}
          &gt;
          {text}-{depth}  
          {  children &amp;&amp; &lt;i className=&quot;fas fa-sort-down&quot;&gt;&lt;/i&gt; || children &amp;&amp; &lt;i className=&quot;fas fa-sort-up&quot;&gt;&lt;/i&gt;} 
      &lt;/button&gt;
        
    {/*THIS IS THE ELEMENT BEING ANIMATED:*/}
    &lt;div className={`frame depth-${depth}`} ref={frame}&gt;
      
          {showChildren &amp;&amp; children &amp;&amp; children.map(item =&gt; {
            return (
              &lt;li key={uuid()}&gt;
                  &lt;ListItem item={item} depth={depth + 1}/&gt;
              &lt;/li&gt;)
            })
        }
      
      &lt;/div&gt;
   &lt;/ol&gt;
    )
  }
  
  class App extends React.Component {
  
  
    render() {
      return (
        &lt;div&gt;
           &lt;ListItem key={uuid()} item={data} depth={0} /&gt;
       &lt;/div&gt;
      );
    }
  }
  
  function render() {
    ReactDOM.render(&lt;App /&gt;, document.getElementById(&quot;root&quot;));
  }
  
  render();
  </code></pre>