(V) Dismissible List
What we're going to be building
Why Though?
We just built a cool dismissible list in the previous recipe, and surely you're thinking,
why Gaurav? why are we rebuilding our component again? Well, we're not, open your web-inspector,
and take a closer look at the dom tree, doesn't it seem strange, that our list, which we dismissed,
still exists in the tree? 
Now we're getting closer to the issue, as you see, useSpring is very powerful while building UI centric animations,
and handling static data. Sure, with some smart logic, you can easily make it handle dynamic data as well, but according
to the react-spring docs we have a better API hook provided by the library to help us elegantly handle dynamic
and complex lists of data.
Hero of this Recipe
The useTransition api hook, is designed to elegantly handle enter and exit transitions for components based on dynamic list of data.
Essentially, react-spring lets us avoid choppy behaviour, while mounting and unmounting our components based on data.
NOTE: We will use some hacky solutions to make useTransition work according to our specifications.
Code (Part 1)
type Props = {
  onDismiss: () => void;
} & PropsWithChildren;
const Dismissible = (props: Props) => {
  const [spring, api] = useSpring(() => ({
    from: {
      x: 0,
      height: 80,
      scale: 0,
    },
    config: config.stiff,
  }));
  const bind = useDrag(
    ({ down, movement: [mx], velocity: [velocity], direction: [x] }) => {
      let flingIt = false;
      if (!down && velocity > 0.5 && x === 1) {
        flingIt = true;
      }
      api.start(() => {
        if (flingIt) {
          return {
            x: 400,
            height: 300,
            scale: 300,
            onResolve: props.onDismiss,
          };
        } else if (spring.x.get() >= 0) {
          return {
            x: down ? mx : 0,
            height: down ? mx : 80,
            scale: down ? mx : 0,
          };
        } else if (!down) {
          return {
            x: 0,
            height: 80,
            scale: 0,
          };
        }
      });
    }
  );
  const height = spring.height.to({
    map: Math.abs,
    range: [160, 280],
    output: [80, 0],
    extrapolate: "clamp",
  });
  const scale = spring.scale.to({
    map: Math.abs,
    range: [0, 280],
    output: [0, 1],
    extrapolate: "clamp",
  });
  const commonProps = {
    borderRadius: 10,
    touchAction: "none",
  };
  return (
    <animated.div
      {...bind()}
      style={{
        x: spring.x,
        height,
        width: 160,
        backgroundColor: "#ff6d6d",
        position: "relative",
        ...commonProps,
        marginBottom: "1rem",
      }}
    >
      <animated.div
        style={{
          height,
          width: 80,
          scale,
          backgroundColor: "#ff6",
          position: "absolute",
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          overflow: "hidden",
          ...commonProps,
        }}
      >
        <animated.div
          style={{
            scale,
            fontSize: "2rem",
            color: "black",
          }}
        >
          {props.children}
        </animated.div>
      </animated.div>
    </animated.div>
  );
};
export default Dismissible;
Code Breakdown
Finding the similarities:
This code, looks quite similar to the Dismissible, we just built. So then, what's changed? If you take a look at the code, you will see, we're using
useSpringinstead ofuseSprings, since now, our goal, is to make a reusable, atomic dismissible, and we're remdering a single component, instead of a component array.Logic Required for Reusability
Since the only goal of our component is to be dismissed, we add a
onDismisscallback handler to the props. We will discuss when and where to fire this callback down below.type Props = {
onDismiss: () => void; // <--- this
} & PropsWithChildren;Handling the Dismiss Action UI state
In Recipe 4 we wanted to track state of dismiss action triggered across the component, and thus we used
useStateto keep track of the same. In the current case though, we simply need to know about dismiss action triggered , over the course of the callback function insidedraghandle. We've used a simple flag, aptly namedflingIt. That is set totrue, if the user flings the dismissible.let flingIt = false; // <--- this
if (!down && velocity > 0.5 && x === 1) {
flingIt = true;
}Handling the Dismiss Action Callback
The other major change, that might not be very visible to the eye, is when we've fired our
onDismisscallback. This last but not the least, change is very important in explaining how react-spring actually handles animations and why it is said to be an optimised way to handle animations.
As you can see, we're firing ouronDismisscallback,onResolveof the animation. This is because, the animation controllers are async in nature, and do not block the main code execution. Sinceasyncfunctions are Promises, they need to berejectedorresolved, in order for the main thread to take notice.if (flingIt) {
return {
x: 400,
height: 300,
scale: 300,
onResolve: props.onDismiss, // <--- this
};
}
Conclusion (Part 1)
That about explains the changes we have made, to the Dismissible to make it more reusable.
There are a few other changes done as well, but you should be able to easily grasp them, since we have
covered all of the same in previous recipes. You can find the list of the same here.
Lets now move onto the useTransition hook for the actual list rendering.
Code (Part 2)
type Props = {
  initialList: string[];
};
const DismissibleList = (props: Props) => {
  const [list, setList] = useState<string[]>([]);
  const transitions = useTransition(list, {
    from: { maxHeight: 0 },
    enter: { maxHeight: 80 },
    leave: { maxHeight: 0 },
    trail: 200 / list.length,
    config: config.stiff,
  });
  useEffect(() => {
    setList(props.initialList);
  }, [props.initialList]);
  return transitions((styles, item) => (
    <animated.div
      style={{
        ...styles,
        marginBottom: styles.maxHeight.to({
          map: Math.abs,
          range: [0, 80],
          output: [0, 10],
          extrapolate: "clamp",
        }),
      }}
      key={item}
    >
      <Dismissible
        onDismiss={() => {
          setList((list) => list.filter((itm) => itm !== item));
        }}
      >
        {item}
      </Dismissible>
    </animated.div>
  ));
};
export default DismissibleList;
Code Breakdown
Definition Enter/Exit Transitions and Component State
As you can see in the code-block below, we're defining an internal state for a list, for
useTransitionhook to use. You might ask, why is this important? The answer lies, in how useTransition tracks list data, since we want to be able to replace and reuse our component, more than once, we need to showuseTransition, that our list at some point will be empty, and only then, new data will be accepted by theuseTransitionhook. (This is part 1 of our hack for reusable list)We're also defining parameters for the
useTransitionhook:- We've seen 
frombe used before in previous recipes, this parameter is simply used to define the initial state of the list-item enterisuseTransition's equivalent totoinuseSpring, it is the state, onto which the list-item will transition to.leaveis a special parameteruseTransitionuses to unmount a list-item from the DOM tree with a transition.trailis also a special parameter, which allows delay to be introduced, during the mounting of list-item.- We've seen 
configbe used before, it allows our defined transitions to use spring physics. 
const [list, setList] = useState<string[]>([]);
const transitions = useTransition(list, {
from: { maxHeight: 0 },
enter: { maxHeight: 80 },
leave: { maxHeight: 0 },
trail: 200 / list.length,
config: config.stiff,
});
useEffect(() => {
setList(props.initialList);
}, [props.initialList]);- We've seen 
 Component Rendering
The component render, is pretty similar to how
useSpringswants components to be rendered, except thatuseTransitionactually wants us to render the same component as a list, thus it takes creates a function api for us to use, which takes our list-item JSX as function parameter.return transitions((styles, item) => (
<animated.div
style={{
...styles,
marginBottom: styles.maxHeight.to({
map: Math.abs,
range: [0, 80],
output: [0, 10],
extrapolate: "clamp",
}),
}}
key={item}
>
<Dismissible
onDismiss={() => {
setList((list) => list.filter((itm) => itm !== item));
}}
>
{item}
</Dismissible>
</animated.div>
));
Conclusion (Part 2)
Phew! That was a lot of information! I hope you found it useful, this is us barely scratching the surface with the potential of react-spring as
a animation library for React. There's a lot more you can do, but most of the components you will build, will end up using this core logic, thus make
sure to use this as a reference while building your projects! Cheers!