Animated.Value

Animated.Value and Animated.ValueXY

An Animated.Value is a number that an animation drives and a style reads. It is not React 7,897 state: animating it re-renders nothing. Create one with useAnimatedValue(0) (shorthand for useRef(new Animated.Value(0)).current) and pass it into an Animated.View's style. Animated.ValueXY pairs x and y, with getTranslateTransform() and getLayout() to turn the pair into a style.

src/anim/ValueDemo.tsx: one value for opacity, a ValueXY for position (excerpt)TSX
  const opacity = useAnimatedValue(0);            // one number, kept across renders
  const pos = useRef(new Animated.ValueXY({ x: 0, y: 0 })).current;   // two numbers
  useEffect(() => {
    let frames = 0;
    const id = opacity.addListener(() => { frames += 1; });
    pos.setOffset({ x: 20, y: 0 });               // added to whatever the animation sets
    Animated.parallel([
      Animated.timing(opacity, { toValue: 1, duration: 400, useNativeDriver: true }),
      Animated.timing(pos, { toValue: { x: 100, y: 40 }, duration: 400,
        useNativeDriver: true }),
    ]).start(({ finished }) => {
      log('finished:', finished, '| opacity updates seen by JS:', frames);
      pos.flattenOffset();                        // offset merged into the value
      pos.stopAnimation((xy) => log('pos after flattenOffset:', JSON.stringify(xy)));
      opacity.removeListener(id);
    });

log(), from src/anim/log.ts, prefixes the milliseconds since the demo started:

Output of 89
 LOG  +837ms finished: true | opacity updates seen by JS: 5
 LOG  +903ms pos after flattenOffset: {"x":120,"y":40}

setValue() jumps without animating; setOffset() adds a base the animation ignores, which lets a drag continue from where the last one ended, and flattenOffset() folds it in (20 + 100 = 120); stopAnimation(cb) stops and reports the current value. A listener hears only what the native side reports: 5 updates for a 400 ms animation that took 837 ms on this busy emulator, so never drive logic from a native animation's listener.