Search code examples
reactjsreact-spring

Initial animation on react-spring not working


I'm using react-spring to animate the transition of opening and closing an accordion component that reveals some text. Using this example on the documentation I was able to come up with a simpler version that creates a transition for the height and opacity:

function CollapseListItem({ title, text }: CollapseItemType) {
  const [isOpen, setIsOpen] = useState(false);
  const [ref, { height: viewHeight }] = useMeasure();

  const { height, opacity } = useSpring({
    from: { height: 0, opacity: 0 },
    to: {
      height: isOpen ? viewHeight : 0,
      opacity: isOpen ? 1 : 0
    }
  });

  const toggleOpen = () => {
    setIsOpen(!isOpen);
  };

  return (
    <>
      <button onClick={toggleOpen}>
        {title} click to {isOpen ? "close" : "open"}
      </button>
      <animated.div
        ref={ref}
        style={{
          opacity,
          height: isOpen ? "auto" : height,
          overflow: "hidden"
        }}
      >
        {text}
      </animated.div>
    </>
  );
}

The issue is that the height transition is only being shown when you close the accordion, when you open the accordion the text suddenly appears, but on the code I can't seem to find why it only works on close, I've tried to hardcode some viewHeight values but I've had no luck.

Here's a code sandbox of what I have


Solution

  • After checking more examples, I realized that I was putting the ref on the wrong component, this change solve the issue:

    function CollapseListItem({ title, text }: CollapseItemType) {
      const [isOpen, setIsOpen] = useState(false);
      const [ref, { height: viewHeight }] = useMeasure();
    
      const props = useSpring({
        height: isOpen ? viewHeight : 0,
        opacity: isOpen ? 1 : 0
      });
    
      const toggleOpen = () => {
        setIsOpen(!isOpen);
      };
    
      return (
        <>
          <button onClick={toggleOpen}>
            {title} click to {isOpen ? "close" : "open"}
          </button>
          <animated.div
            style={{
              ...props,
              overflow: "hidden"
            }}
          >
            <div ref={ref}>{text}</div>
          </animated.div>
        </>
      );
    }
    

    Here's the full solution in case anyone is also trying to build an animated accordion / collapse using spring.