pmndrs / react-spring

✌️ A spring physics based React animation library
http://www.react-spring.dev/
MIT License
27.73k stars 1.18k forks source link

[feat]: UseSprings for Object #2268

Open grifdail opened 3 months ago

grifdail commented 3 months ago

A clear and concise description of what the feature is

an API, similar to useSpring but taking an object ({[key:string]: any }) instead of an number and that return a map of SpringValue. Those spring value are uniquely associated to the key. If a new one is added to the object, a new SpringValue is created, if a key is removed, the associated SpringValue is also deleted.

Why should this feature be included?

Currently, useSprings accept a number and will recreate spring when that count change. This means every spring is bound to an index. This proposal would allow for spring to be bound to a key. This would means, that when an item is deleted, it's associated spring is also deleted. In the current system, if we pair each spring to the correspoding index, we get bug like this:

  const nodes = Object.values(tree.nodes);
  const [nodePositionSpring, nodePositionSpringApi] = useSprings(
    nodes.length,
    (index) => {
      return {
        to: { xy: [nodes[index].positionX, nodes[index].positionY] },
      };
    },
    [nodes]
  );

Animation (13)

The most recent elements get the spring value of the elements that just got deleted.

Why not useSpring inside the item itself ?

In the above example, you can see the Green Edge linking the two nodes. These edge are dependent of the position of the two node, therefore they need to be aware of the position of two nodes. They also have to be updated as the nodes moves so we need the spring value.

edges.map((edge) => {
          return <Edge start={nodePositionSpring[edge[0]]} end={nodePositionSpring[edge[1]]}} />;
        })

We could, in theorie use refs to get the value for the nodes themself but that's just moving the problems.

Why not useTransition?

useTransition return a function which we cannot use to animate the Edge at the same time as the Nodes, but i agree that, in theory, it would be great if there was a way to access the springValue themselves.

Please provide an example for how this would work

There's many possibles for for the api.

// function useSprings(map: {[key:string]: ConfigObject)): [{[key:string]: SpringValue}, SpringRef]
const [springs, api] = useSprings({key: {to: {opacity: 0}}); 
// function useSprings(keys: string[]: config: (key: string) => ConfigObject)): [{[key:string]: SpringValue}, SpringRef]
const [springs, api] = useSprings(nodes, key => ({to: nodes[key].opacity));