S
S
sasha_jarvi2020-06-30 21:16:20
React
sasha_jarvi, 2020-06-30 21:16:20

How to fix map centering issue (react-native-maps) when tapping on a marker?

I have the following map component using react-native-maps:

const TasksMapScreen: React.FunctionComponent<Props> = () => {
  const route = useRoute();

  const tasks = [
    {
      id: 5602481,
      category: 'lights',
      text: 'задача 1',
      location: 'адрес 1',
      latlng: {
        latitude: 54.1925489,
        longitude: 37.6705917,
      },
      distance: 200,
      date: '08.06.2019',
      price: 300,
      isResolved: false,
      reworkings: 0,
    },
    {
      id: 5602482,
      category: 'garbage',
      text: 'задача 2',
      location: 'адрес 2',
      latlng: {
        latitude: 54.1541381,
        longitude: 37.5843054,
      },
      distance: 200,
      date: '08.06.2019',
      price: 0,
      deadline: '18.06.2019',
      isResolved: false,
      reworkings: 0,
    },
    {
      id: 5602483,
      category: 'garbage',
      text: 'задача 3',
      location: 'адрес 3',
      latlng: {
        latitude: 54.1879952,
        longitude: 37.5988459,
      },
      distance: 200,
      date: '08.06.2019',
      price: 0,
      deadline: '18.06.2019',
      isOverdue: true,
      isResolved: false,
      reworkings: 0,
    },
    {
      id: 5602484,
      category: 'lights',
      text: 'задача 4',
      location: 'адрес 4',
      latlng: {
        latitude: 54.1571578,
        longitude: 37.5970531,
      },
      distance: 200,
      date: '08.06.2019',
      price: 0,
      deadline: '18.06.2019',
      rating: 5,
      isResolved: true,
      reworkings: 2,
    },
    {
      id: 5602489,
      category: 'lights',
      text: 'адрес 5',
      location: 'задача 5',
      latlng: {
        latitude: 54.1886121,
        longitude: 37.5982382,
      },
      distance: 200,
      date: '08.06.2019',
      price: 0,
      isResolved: false,
      reworkings: 0,
    },
  ];

  const tasksObj = tasks.reduce((acc, task) => {
    acc[task.id] = task;
    return acc;
  }, {});

  const tasksFirstId = Object.keys(tasksObj)[0];
  const initialMarkerLatlng = tasksObj[tasksFirstId].latlng;
  const taskId = route.params?.taskId;

  const [zoom, setZoom] = useState(14);
  const [task, selectTask] = useState(null);

  const region = {
    ...initialMarkerLatlng,
    latitudeDelta: 0.0922,
    longitudeDelta: 0.0421,
  };

  const mapView = useRef(null);

  useEffect(() => {
    if (taskId) {
      selectTask(taskId);
    }

    if (mapView.current) {
      mapView.current?.animateCamera(
        {
          center: tasksObj[task].latlng,
          zoom: 15,
        },
        1000,
      );
    }
  }, [taskId, task, tasksObj]);

  return (
    <View style={styles.container}>
      <MapView
        ref={mapView}
        style={styles.map}
        minZoomLevel={zoom}
        maxZoomLevel={zoom}
        showsCompass={false}
        initialRegion={region}>
        {Object.values(tasksObj).map((task) => (
          <Marker
            coordinate={task.latlng}
            title=""
            key={task.id}
            onPress={() => selectTask(task.id)}>
            <TaskMarker
              category={task.category}
              price={task.price}
              isResolved={task.isResolved}
              reworkings={task.reworkings}
            />
          </Marker>
        ))}
      </MapView>
    </View>
  );
};


The id property comes from another component, on the basis of which the task is selected, on the marker of which the map is centered. This aspect is fine. However, changing the state and centering should also happen when tapping on the task marker, which is currently not the case. I believe that the point is in the first three lines insideuseEffect()

if (taskId) {
      selectTask(taskId);
    }


However, when they are removed, centering stops working. Removal selectTask(taskId)causes an error "Too many re-renders". How to solve the problem of changing the state and then centering when tapping on a marker?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
S
sasha_jarvi, 2020-07-14
@sasha_jarvi

I changed the code in this way - I added the second one to the code useEffect()to change the state taskbased on the incoming idone, while the first one useEffect()remains responsible for centering the map based on the existing one task:

const [zoom, setZoom] = useState(14);
  const [task, selectTask] = useState(undefined);

  const mapView = useRef(null);

  useEffect(() => {
    if (task) {
      if (mapView.current) {
        mapView.current?.animateCamera(
          {
            center: task.latlng,
            zoom: 15,
          },
          1000,
        );
      }
    }
  }, [task]);

  useEffect(() => {
    if (route.params?.taskId) {
      // @ts-ignore
      selectTask(tasks.find((t) => t.id === route.params.taskId));
    }
  }, [route.params]);

  return (
    <View style={styles.container}>
      <MapView
        ref={mapView}
        style={styles.map}
        minZoomLevel={zoom}
        maxZoomLevel={zoom}
        showsCompass={false}
        initialRegion={region}
        onPress={() => selectTask(undefined)}>
        {tasks.map((task) => (
          <Marker
            coordinate={task.latlng}
            title=""
            key={task.id}
            // @ts-ignore
            onPress={() => selectTask(task)}>
            <TaskMarker
              category={task.category}
              price={task.price}
              isResolved={task.isResolved}
              reworkings={task.reworkings}
            />
          </Marker>
        ))}
      </MapView>
    </View>
  );
};

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question