Stanko / react-animate-height

Lightweight React component for animating height using CSS transitions. Slide up/down the element, and animate it to any specific height.
https://muffinman.io/react-animate-height
MIT License
758 stars 53 forks source link

Question - animate on initial render #102

Closed charleshimmer closed 4 years ago

charleshimmer commented 4 years ago

this looks to be what I was looking for but is there a way to animate the height from 0 to auto when a component is initially rendered? I'm sure I could do this manually with some state that I trip after the component is rendered, but curious if there is a better way to handle this case.

Stanko commented 4 years ago

You need to render two different states, like you said, you need to change state. I recommend using requestAnimationFrame in componentDidMount.

charleshimmer commented 4 years ago

So for my use case I have a list of child elements to be rendered. Items can be removed / added to this list at any time. When an item is added, I need to animate it's height from 0 to auto. When an item is removed, go from auto to 0.

What I am doing currently is something like this (using React Hooks):

Basically for every item I need to render I initialize it with a height value of 0, but then setState to set it to auto thereafter. How would you recommend I use requestAnimationFrame?

type RowHeightMap = { [key: number]: boolean };

const [rowHeight, setRowHeight] = React.useState<RowHeightMap>({});

React.useEffect(() => {
  if (!items) {
    return;
  }
  setRowHeight(
    field.value.reduce((acc: any, _: any, index: number) => {
      acc[index] = true;
      return acc;
    }, {})
  );
  }, [items]);

return (
  <div>
    {items.map((row, index) => (
      <AnimateHeight
        key={row.__key}
        duration={200}
        height={rowHeight[index] ? 'auto' : 0}
        easing="cubic-bezier(0.25, 0.25, 0.25, 1)"
        animateOpacity
      >
        {row}
      </AnimateHeight>
    )}
  </div>
);
Stanko commented 4 years ago

To animate on add, I would create ListItem which takes care of itself. Removing items is a little bit more complicated. If you don't want to handle states yourself, you might want to check https://github.com/reactjs/react-transition-group as it is outside of the scope of this library.

Hope that helps!

charleshimmer commented 4 years ago

Yeah I'm familiar with react transition groups, but it suffers from the limitation of not being able to handle the animate to height: auto. What I have above works, I just wasn't sure if there was a better way I was overlooking.

Not sure how ListItem takes care of itself? Wouldn't it still need to start at height 0, then be flipped to height auto after initial mount? Removing was as simple as delaying the actual removal from the list till after the animation has ran. Like this:

  const removeRow = (remove: any, i: number) => {
    if (field.value.length > 1) {
      const newValue = { ...rowHeights };
      delete newValue[i];
      setRowHeights(rowHeights);
      setTimeout(() => remove(i), 200);
    }
  };
Stanko commented 4 years ago

It is a little bit hard to explain so I create an example: https://codesandbox.io/s/react-animated-list-example-made-using-react-animate-height-je9q1?file=/src/App.js

It is not really a straight forward task, so there is hefty amount of state management we have to do on our own.

I made it using classes, as I'm still not super comfortable with hooks 🙈

Cheers!

P.S. I might add this to the documentation

charleshimmer commented 4 years ago

Thanks for taking the time to write up a demo. We are basically doing the same thing except you have your organized a bit better with a List component and ListItem components. And I'm using hook's equivalent to componentDidMount to flip the height from 0 to auto where you're using requestAnimationFrame.

I'll probably tweak mine a bit based on some of your example's ideas. Thanks again!

Stanko commented 4 years ago

You are welcome, glad it worked! It is abstract and a little bit hard to explain so I thought example would help! Cheers!

charleshimmer commented 4 years ago

Yep def did. I ended up using requestAnimationFrame for mine as well and ended up making a ListItem like component which cleaned up the code a bit.