hardReact Native#37

Animated event with PanResponder

Prompt

Create a draggable View using PanResponder and Animated.Value for position. Track both x and y translation as the user drags their finger.

Solution

const pan = useRef(new Animated.ValueXY()).current
const panResponder = useRef(PanResponder.create({
  onMoveShouldSetPanResponder: () => true,
  onPanResponderMove: Animated.event([null, { dx: pan.x, dy: pan.y }], { useNativeDriver: false }),
  onPanResponderRelease: () => {
    pan.extractOffset()
  },
})).current
return <Animated.View style={[pan.getLayout(), { width: 80, height: 80, backgroundColor: 'red' }]} {...panResponder.panHandlers} />
Mentor's take

This challenge wires two systems together. PanResponder is the ergonomic wrapper over RN's gesture responder negotiation — the arbitration protocol deciding which view owns a touch. onMoveShouldSetPanResponder: () => true claims the gesture once the finger moves; the negotiation re-runs on every move, which is how a wrapping ScrollView can steal your touch mid-drag. That's why production responders also implement onPanResponderTerminate — handling only release ships gestures that freeze when ownership is taken away.

The Animated side is a pipeline, not a handler: Animated.event([null, { dx: pan.x, dy: pan.y }]) declaratively maps the gesture's per-move deltas into the ValueXY — no per-event JS callback body, no setState. extractOffset() on release is the piece candidates fumble: it folds the accumulated value into the offset and zeroes the value, so the next gesture's deltas (which always start at 0) continue from where the box rests instead of snapping back to origin.

useNativeDriver: false is forced twice over — getLayout() animates left/top, layout props the native driver can't drive, and PanResponder events are delivered on the JS thread anyway. That's the honest limitation to volunteer: recognition and tracking both stall when JS is busy, which is precisely why production gesture code is react-native-gesture-handler + Reanimated — native-thread recognition, worklet-driven tracking. PanResponder remains the right mental model and the zero-dependency fallback.

Red flag: no extractOffset/offset handling (box teleports to origin on the second drag) or omitting termination handling — both are "wrote it, never shipped it" tells.

Say it: "PanResponder is JS-thread gesture arbitration — Animated.event maps deltas into a ValueXY and extractOffset banks position between gestures — and when recognition must survive a busy JS thread, I move to gesture-handler with Reanimated."