javascript – How can I use React Material UIs transition components to animate adding an item to a list?

javascript – How can I use React Material UIs transition components to animate adding an item to a list?

I updated your Sandbox code to achieve what you wanted, but I dont think MaterialUI is the best library for that (I could be missing a better way to do it).

The challenge is that when you add a new item, that doesnt exist in the DOM yet. And most of those animation libraries/components require the element to be in the DOM and they just hide and show it with a transition time.

I had a similar situation and after some research, the better library I found that can handle animation for elements that are not yet in the DOM, was the Framer Motion. (You can check their documentation for mount animations)

Anyway, here is the link for the new Code Sandbox so you can take a look. The changes I made:

Removed random key

In the map function that creates your list using the <Collapse /> component, there was a function to get a random integer and assign that as a key to your component. React needs to have consistent keys to properly do its pretenders, so removing that random number fixes the issue where your Toggle button wasnt animating properly. (If your list of items doesnt have an unique ID, just use the index of the map function, which is not a good solution, but still better than random numbers).

<Collapse key={i} timeout={this.state.collapseTimeout} in={this.state.open}>
    {it}
</Collapse>

Added a new function to control the toggle

The approach here was: add the item in your list and, after the element is in the DOM, close the <Collapse />, wait a little bit and open it again (so you can visually see the animation). In order to do that, we needed a new toggle function that can explicit set the value of the collapse.

toggleValue(value) {
  this.setState(() => {
    return {
      open: value
    };
  });
}

Added a variable timeout for the collapse

The last issue was that, closing the <Collapse /> when the new item is added, was triggering the animation to close it. The solution here was to dynamically change the timeout of the collapse, so you dont see that.

setCollapseTimeout(value) {
  this.setState(() => {
    return {
      collapseTimeout: value
    };
  });
}

When adding the element to the list, wait to trigger the animation
Again, to work around the issue with elements not yet in the DOM, we need to use a setTimeout or something to wait to toggle the <Collapse />. That was added in your add() function.

add() {
  this.toggleValue(false);
  this.setCollapseTimeout(0);
  this.setState(prev => {
    const n = prev.items.length;
    return {
      items: [<li key={n}>Hello, World {n}!</li>, ...prev.items]
    };
  });
  setTimeout(() => {
    this.setCollapseTimeout(300);
    this.toggleValue(true);
  }, 100);
}

Again, this is a hacky solution to make <Collapse /> from MaterialUI work with elements that are not yet in the DOM. But, as mentioned, there are other libraries better for that.

Good luck đŸ™‚

Ended up here earlier on and then came back to create a sandbox showing hopefully a simple method for this scenario. The material-ui docs are a bit (lot) light in this area and I was fighting with a very similar situation, but I tried something with TransitionGroup from react-transition-group, crossed my fingers and it seemed to work.

Forked CodeSandbox with TransitionGroup

The gist is that you

  1. wrap all of the components you want to transition in the <TransitionGroup> component
  2. Inside the TransitionGroup, put in the condition (logic or loop output) for the data you want to render
  3. Wrap the individual components you want to transition with transition component of your choice – <Collapse> in this example

e.g. In its most simple setup where items is an array of unique numbers coming from either props, state or a redux store

<TransitionGroup>
  {items.map(item => (
      <Collapse key={item}>
          I am item {item}
      </Collapse>
  ))}
</TransitionGroup>

With this setup I have found that I didnt need to put any props on the TransitionGroup or Collapse, and the TransitionGroup handled all the mounting and unmounting in the loop rendering. Material UI doesnt produce the lightest of HTML output, but I guess its all rendered on the fly so maybe that makes it better (unless you have thousands of elements, then things start to drag).

You can even go a step further and wrap the whole thing in another TransitionGroup to cover situations where you want to remove the whole thing without transitioning all of the individual items – in this instance I switched it to a <Slide>. I was absolutely certain that this wouldnt work, but it seemed to not care. You can also try and be semantic and use the component property rather than wrapping in another element e.g.

<TransitionGroup>
  {items.length > 0 && (
    <Slide>
      <TransitionGroup component=ul>
        {items.map((item) => (
          <Collapse component=li key={item}>I am item {item}</Collapse>
        ))}
      </TransitionGroup>
    </Slide>
  )}
</TransitionGroup>

I have changed the sandbox in the following ways

  • Included TransitionGroup from react-transition-group
  • Changed the add logic so that the components arent part of the items array – the array only contains the data required to render the components
  • I have added a simple count and pushed that to the array to give the items a unique index (had originally used Math.random, but I wanted a prettier output). Generally your items will probably be coming from a database somewhere where a unique id will already be set.
  • Rendered the components in a loop based on the data in the array (this could be done in a separate function, but the gist is that the components arent being stored in the array)
  • added a delete function to show the removal of single items
  • wrapped the whole group in a second <TransitionGroup> to show that the unmounting can happen in a group level
  • Put in some simple styling to get a better idea of the effect. You could use Material UI components here, but just wanted to keep it simple.

Hope this helps someone in the future.

javascript – How can I use React Material UIs transition components to animate adding an item to a list?

Leave a Reply

Your email address will not be published.