7

I am making an app using react-native-svg and I am having trouble to update the component's state from inside a PanResponder.

I have a < Svg > element with a < Circle > in it. The < Circle >'s position is relative to a variable called originCoords (a state variable). I have used the PanResponder to make it so that, when doing a panning gesture, the < Svg > (with the < Circle > inside it) is moved and, once the gesture's ended, the < Svg > goes back to its original position.

I am trying to make the originCoords change once the panning gesture is finished, so, even though the < Svg > goes back to its original position, the < Circle > remains at its new position. But I am having a real hard time changing the originCoords' state from inside the PanResponder, in the createPanResponder.js file, it just stays the same, at least on the android emulator. Any help would be much appreciated.

Here are my files:

// MapScreen.js

import React, { useRef, useState } from "react";
import { Animated } from "react-native";
import { Svg, Circle } from "react-native-svg";
import { createPanResponder } from "../services/utils/createPanResponder";

export const MapScreen = () => {
  const [originCoords, setOriginCoords] = useState({x: 0, y: 0});
  const [circleCoords, setCircleCoords] = useState({x: 20, y: 20});

  const pan = useRef(new Animated.ValueXY()).current;
  const panResponder = createPanResponder(pan, originCoords, setOriginCoords);

  return (
    <Animated.View
      style={{transform: [{ translateX: pan.x }, { translateY: pan.y }],}}
      {...panResponder.panHandlers}
    >
      <Svg width='300' height='300' style={{ backgroundColor: "blue" }}>
        <Circle
          cx={`${circleCoords.x - originCoords.x}`}
          cy={`${circleCoords.y - originCoords.y}`}
          r="5"
          stroke="white"
          strokeWidth="1"
        />
      </Svg>
    </Animated.View>
  );
};
import { useRef } from "react";
import { PanResponder, Animated } from "react-native";

export const createPanResponder = (pan, originCoords, setOriginCoords) => {
  return useRef(
    PanResponder.create({
      // PanResponder default configs have been removed to be more succint
      onPanResponderMove: (evt, gestureState) => {
        // sets pan.x and pan.y to equal gesture's deltas
        Animated.event([{ x: pan.x }])({ x: gestureState.dx });
        Animated.event([{ y: pan.y }])({ y: gestureState.dy });
      },

      onPanResponderRelease: (evt, gestureState) => {
        //resets pan.x and pan.y
        Animated.event([{ x: pan.x }])({ x: 0 });
        Animated.event([{ y: pan.y }])({ y: 0 });

        // PROBLEM IS HERE, setOriginCoords DOESN'T DO ANYTHING
        console.log(originCoords); // { x: 0, y: 0 }
        const { dx, dy } = gestureState;
        setOriginCoords({ ...originCoords, dx, dy });
        console.log(originCoords); // { x: 0, y: 0 }
      },
    })
  ).current;
};

lucsande
  • 91
  • 1
  • 5

1 Answers1

0

Although I don't see the lost cause in making this function a class component, functions don't have state, only classes do. This is from what I learned today from here and the docs

constructor(props) {
    super(props);
    this.pan = new Animated.ValueXY();
    this.panResponder = PanResponder.create({
      onStartShouldSetPanResponder: (e, state) => false,
      onStartShouldSetPanResponderCapture: (e, state) => false,
      onMoveShouldSetPanResponder: () => true,
      onMoveShouldSetPanResponderCapture: (e, { dx, dy }) => {
        if (Math.abs(dx) > 10 || Math.abs(dy) > 10) {
          this.state.holding && this.setState({ holding: false });
          return true;
        } else {
          !this.state.holding && this.setState({ holding: true });
          return true;
        }
      },
      onPanResponderGrant: (e, gestureState) => {
        this.pan.setOffset(this.pan.__getValue());
        this.pan.setValue({ x: e.nativeEvent.pageX, y: e.nativeEvent.pageY });
      },
      onPanResponderMove: (e, gesture) => {
        this.pan.setValue({ x: gesture.dx, y: gesture.dy });
        /*Animated.event([null, { dx: this.pan.x, dy: this.pan.y }])(
          e,
          gestureState
        );*/
        if (Math.abs(gesture.dx) > 10 || Math.abs(gesture.dy) > 10) {
          clearTimeout(this.t);
          this.state.holding && this.setState({ holding: false });
        } else {
          !this.state.holding && this.setState({ holding: true });
        }
      },
      onPanResponderRelease: (e, { vx, dx }) => {
        this.setState({ holding: false });
        const offScreenX = () => {
          if (this.props.width < e.nativeEvent.pageX) {
            Animated.spring(this.pan.x, {
              toValue: this.props.width - 60,
              bounciness: 10
            }).start();
          } else if (0 > e.nativeEvent.pageX) {
            Animated.spring(this.pan.x, {
              toValue: 0,
              bounciness: 10
            }).start();
          }
        };
        const offScreenY = () => {
          if (this.props.height < e.nativeEvent.pageY) {
            Animated.spring(this.pan.y, {
              toValue: this.props.height - 60,
              bounciness: 10
            }).start();
          } else if (0 > e.nativeEvent.pageY) {
            Animated.spring(this.pan.y, {
              toValue: 0,
              bounciness: 10
            }).start();
          }
        };
        // abs(vx) speed (not velocity) of gesture,
        // abs(dx) distance traveled (not displacement)
        //if (Math.abs(vx) >= 0.5 || Math.abs(dx) >= 30) {
        this.pan.flattenOffset();
        //}
        if (this.props.width / 2 > e.nativeEvent.pageX) {
          if (this.props.height / 2 < e.nativeEvent.pageY) {
            offScreenX();
            Animated.spring(this.pan.y, {
              toValue: this.props.height - 60,
              bounciness: 10
            }).start();
          } else {
            offScreenY();
            Animated.spring(this.pan.x, {
              toValue: 0,
              bounciness: 10
            }).start();
          }
        } else {
          if (this.props.height / 2 > e.nativeEvent.pageY) {
            offScreenX();
            Animated.spring(this.pan.y, {
              toValue: 0,
              bounciness: 10
            }).start();
          } else {
            offScreenY();
            Animated.spring(this.pan.x, {
              toValue: this.props.width - 60,
              bounciness: 10
            }).start();
          }
        }
      }
    });
    this.state = { highAndTight: true, hideOpener: true };
  }