<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Victor Olufade</title>
    <description>The latest articles on DEV Community by Victor Olufade (@victor_olufade_241a02b9b0).</description>
    <link>https://dev.to/victor_olufade_241a02b9b0</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1738292%2F83e0ab04-91d6-4c5a-b0ff-b2fc0395a71f.png</url>
      <title>DEV Community: Victor Olufade</title>
      <link>https://dev.to/victor_olufade_241a02b9b0</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/victor_olufade_241a02b9b0"/>
    <language>en</language>
    <item>
      <title>The Typo Typescript Hid From Me</title>
      <dc:creator>Victor Olufade</dc:creator>
      <pubDate>Thu, 11 Jun 2026 11:15:50 +0000</pubDate>
      <link>https://dev.to/victor_olufade_241a02b9b0/the-typo-typescript-hid-from-me-3pbn</link>
      <guid>https://dev.to/victor_olufade_241a02b9b0/the-typo-typescript-hid-from-me-3pbn</guid>
      <description>&lt;p&gt;The Typo That TypeScript Hid From Me&lt;/p&gt;

&lt;p&gt;I spent a good chunk of time debugging a silent failure in a React Native app. I was integrating the Google Routes API v2 to draw polylines and compute distance/duration between two coordinates. The API kept returning an empty object {} despite a 200 OK response.&lt;/p&gt;

&lt;p&gt;After ruling out field mask issues, API key restrictions, and billing problems — all the usual suspects — I added granular logs directly inside the fetch function:&lt;/p&gt;

&lt;p&gt;console.log("START COORDS:", JSON.stringify(start));&lt;br&gt;
console.log("END COORDS:", JSON.stringify(end));&lt;/p&gt;

&lt;p&gt;The output:&lt;/p&gt;

&lt;p&gt;START COORDS: {"latitude":6.450904}&lt;br&gt;
END COORDS: {"latitude":6.4696}&lt;/p&gt;

&lt;p&gt;No longitude. The Routes API received an incomplete request body and returned {} silently.&lt;/p&gt;

&lt;p&gt;But when I logged the raw data from the parent component:&lt;/p&gt;

&lt;p&gt;console.log(schedule?.pickup_coord);&lt;br&gt;
// {"latitude": 6.450904, "longitide": 3.390147}&lt;/p&gt;

&lt;p&gt;There it was — longitide instead of longitude. A missing u.&lt;/p&gt;

&lt;p&gt;Here’s where it gets interesting.&lt;/p&gt;

&lt;p&gt;My custom hook accepted coordinates typed as LatLng:&lt;/p&gt;

&lt;p&gt;type LatLng = {&lt;br&gt;
  latitude: number;&lt;br&gt;
  longitude: number;&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;When the object { latitude: 6.450904, longitide: 3.390147 } was cast with as LatLng, TypeScript didn’t complain. The cast silenced any structural mismatch. At runtime, accessing .longitude on that object returned undefined — and undefined serialized into the request body as a missing field entirely.&lt;/p&gt;

&lt;p&gt;The Routes API received:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "origin": {&lt;br&gt;
    "location": {&lt;br&gt;
      "latLng": {&lt;br&gt;
        "latitude": 6.450904,&lt;br&gt;
        "longitude": undefined&lt;br&gt;
      }&lt;br&gt;
    }&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Which JSON.stringify quietly drops, producing:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "origin": {&lt;br&gt;
    "location": {&lt;br&gt;
      "latLng": {&lt;br&gt;
        "latitude": 6.450904&lt;br&gt;
      }&lt;br&gt;
    }&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;A perfectly valid JSON object. No error thrown. No warning logged. Just a silent wrong request — and a silent empty response.&lt;/p&gt;

&lt;p&gt;The real lesson here isn’t just “check your typos.”&lt;/p&gt;

&lt;p&gt;It’s that as SomeType in TypeScript is an assertion, not a validation. You’re telling the compiler “trust me, this is the right shape” — and it does. Completely. If the runtime data doesn’t match, TypeScript has no way to know.&lt;/p&gt;

&lt;p&gt;The safer pattern is a runtime validator or a mapping function that explicitly pulls the fields you need:&lt;/p&gt;

&lt;p&gt;const toLatLng = (coord: any): LatLng =&amp;gt; ({&lt;br&gt;
  latitude: coord?.latitude,&lt;br&gt;
  longitude: coord?.longitude,&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;This way, even if the upstream data has a typo, the mapping makes the missing field explicit — and you catch a { latitude: 6.450904, longitude: undefined } object before it ever reaches your API call, rather than getting a silent {} response from a server later.&lt;/p&gt;

&lt;p&gt;Two layers failed here: a typo in the data source, and an unchecked as cast that let it through undetected. &lt;/p&gt;

</description>
      <category>api</category>
      <category>javascript</category>
      <category>reactnative</category>
      <category>typescript</category>
    </item>
    <item>
      <title>The Typo Typescript Hid From Me</title>
      <dc:creator>Victor Olufade</dc:creator>
      <pubDate>Thu, 11 Jun 2026 11:04:39 +0000</pubDate>
      <link>https://dev.to/victor_olufade_241a02b9b0/the-typo-typescript-hid-from-me-2lmf</link>
      <guid>https://dev.to/victor_olufade_241a02b9b0/the-typo-typescript-hid-from-me-2lmf</guid>
      <description></description>
      <category>javascript</category>
      <category>programming</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Typo That TypeScript Hid From Me

I spent a good chunk of time debugging a silent failure in a React Native app. I was integrating the Google Routes API v2 to draw polylines and compute distance/duration between two coordinates. The AP</title>
      <dc:creator>Victor Olufade</dc:creator>
      <pubDate>Thu, 11 Jun 2026 11:03:06 +0000</pubDate>
      <link>https://dev.to/victor_olufade_241a02b9b0/the-typo-that-typescript-hid-from-me-i-spent-a-good-chunk-of-time-debugging-a-silent-failure-in-2jjk</link>
      <guid>https://dev.to/victor_olufade_241a02b9b0/the-typo-that-typescript-hid-from-me-i-spent-a-good-chunk-of-time-debugging-a-silent-failure-in-2jjk</guid>
      <description></description>
      <category>api</category>
      <category>reactnative</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Draw your custom polylines for directions on react-native-maps with Google Maps' Routes API and handle rerouting.</title>
      <dc:creator>Victor Olufade</dc:creator>
      <pubDate>Thu, 07 Aug 2025 10:38:12 +0000</pubDate>
      <link>https://dev.to/victor_olufade_241a02b9b0/draw-your-custom-polylines-for-directions-on-react-native-maps-with-google-maps-routes-api-and-1fj4</link>
      <guid>https://dev.to/victor_olufade_241a02b9b0/draw-your-custom-polylines-for-directions-on-react-native-maps-with-google-maps-routes-api-and-1fj4</guid>
      <description>&lt;p&gt;While working on a carpooling application over the past months using react-native, I had to use react-native-maps and react-native-maps-directions for map views and display of directions between locations.&lt;/p&gt;

&lt;p&gt;Under the hood react-native-maps-directions used Google's directions api to display polylines on the map. By Mar 1, 2025, Google decided to move the Directions API to legacy status and this meant Google Map apiKeys obtained after this date would no longer have access to the Directions API.&lt;/p&gt;

&lt;p&gt;Unfortunately I was utilizing a temporary apiKey before that date. By the time a billed key was provided, I could no longer utilize the react-native-maps-directions library. Google then replaced the Directions API with Routes API. To solve the problem, I had to write a custom hook that used the Routes API and the "@mapbox/polyline" library.&lt;/p&gt;

&lt;p&gt;The implementation is shared here. This is not necessarily in the most optimized state. You can further customize as you deem fit. But this works and even has a rerouting logic integrated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState, useEffect, useRef } from "react";
import { Polyline, LatLng } from "react-native-maps";
import polyline from "@mapbox/polyline";
import { theme } from "@src/theme/theme";
import { scale } from "react-native-size-matters";
import { useDebounce } from "use-debounce";
import { convertSecondsToMinutes } from "@src/utils/general";
import { haversineDistance } from "@src/utils/maps";

interface MapWithRoutesProps {
  origin: LatLng;
  destination: LatLng;
  apiKey: string;
  strokeColor?: string;
  strokeWidth?: number;
  refetchLocation?: LatLng | undefined;
  updateRouteInfoThreshold?: number; // Distance threshold in meters to trigger reroute
  onRouteReady?: (coords: LatLng[]) =&amp;gt; void;
}

interface RouteResponse {
  routes?: {
    polyline: {
      encodedPolyline: string;
    };
    distanceMeters: number;
    duration: string;
  }[];
}

const UseMapWithRoutes = ({
  origin,
  destination,
  apiKey,
  strokeColor = theme?.colors?.screens?.mapScreen?.directionsStroke,
  strokeWidth = scale(5),
  refetchLocation,
  updateRouteInfoThreshold = 100, // Default threshold of 50 meters
  onRouteReady,
}: MapWithRoutesProps) =&amp;gt; {
  const [polylineCoordinates, setPolylineCoordinates] = useState&amp;lt;LatLng[]&amp;gt;([]);
  const [distanceTimeObj, setDistanceTimeObj] = useState({
    distanceMeters: 0 as number | undefined,
    duration: "",
  });

  const [justSetPolyline, setJustSetPolyline] = useState(false)
  const triggerSetPolyline = (bool: boolean) =&amp;gt;
    setJustSetPolyline((prev) =&amp;gt; bool);

  const [refetchNow, setRefetchNow] = useState(false);
  const triggerImmediateRefetch = (bool: boolean) =&amp;gt;
    setRefetchNow((prev) =&amp;gt; bool);

  const isRerouting = useRef(false);

  const checkDeviation = (
    currentLocation: LatLng,
    polylineCoords: LatLng[]
  ) =&amp;gt; {
    if (!polylineCoords || polylineCoords.length === 0) return false;

    // Find the closest point on the polyline to the current location
    const closestPointDistance = Math.min(
      ...polylineCoords.map((point) =&amp;gt;
        haversineDistance(currentLocation, point)
      )
    );

    // Check if the distance exceeds the threshold
    return closestPointDistance &amp;gt; updateRouteInfoThreshold;
  };

  const handleReroute = async (currentLocation: LatLng) =&amp;gt; {
    if (isRerouting.current) return; // Prevent multiple reroute requests
    isRerouting.current = true;

    try {
      await fetchRoutes(currentLocation, {
        latitude: destination?.latitude,
        longitude: destination?.longitude,
      });
    } finally {
      isRerouting.current = false;
    }
  };

  const fetchRoutes = async (
    start: LatLng,
    end: LatLng,
    refetchDistance?: boolean
  ) =&amp;gt; {
    console.log("called fetchRoutes?&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;");

    if (!start || !end) {
      return;
    }
    try {
      const response = await fetch(
        `https://routes.googleapis.com/directions/v2:computeRoutes?key=${apiKey}`,
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "X-Goog-FieldMask":
              "routes.polyline.encodedPolyline,routes.distanceMeters,routes.duration",
          },
          body: JSON.stringify({
            origin: {
              location: {
                latLng: {
                  latitude: start.latitude,
                  longitude: start.longitude,
                },
              },
            },
            destination: {
              location: {
                latLng: {
                  latitude: end.latitude,
                  longitude: end.longitude,
                },
              },
            },
            travelMode: "DRIVE",
            routingPreference: "TRAFFIC_AWARE",
          }),
        }
      );

      const data: RouteResponse = await response.json();

      if (!data || !data?.routes) return;

      if (refetchDistance === true) {
        setDistanceTimeObj((prev) =&amp;gt; ({
          ...prev,
          distanceMeters:
            data.routes![0].distanceMeters !== undefined
              ? Math.round(data.routes![0].distanceMeters / 1000)
              : 0,
          duration: convertSecondsToMinutes(data.routes![0].duration),
        }));
        return;
      }

      if (data.routes[0].polyline.encodedPolyline) {
        const encodedPolyline = data.routes[0].polyline.encodedPolyline;
        const decodedCoordinates = decodePolyline(encodedPolyline);
        setPolylineCoordinates(decodedCoordinates);
        triggerSetPolyline(true)
        data.routes &amp;amp;&amp;amp; data.routes?.length &amp;gt; 0
          ? setDistanceTimeObj((prev) =&amp;gt; ({
              ...prev,
              distanceMeters:
                data.routes![0].distanceMeters !== undefined
                  ? Math.round(data.routes![0].distanceMeters / 1000)
                  : undefined,
              duration: convertSecondsToMinutes(data.routes![0].duration),
            }))
          : null;
        if (onRouteReady) onRouteReady?.(decodedCoordinates);
        return decodedCoordinates;
      }
    } catch (error) {
      console.error("Error fetching routes:", error);
    }
  };

  useEffect(() =&amp;gt; {
    if (
      destination?.latitude &amp;amp;&amp;amp;
      origin?.latitude &amp;amp;&amp;amp;
      (!polylineCoordinates || polylineCoordinates.length === 0)
    ) {
      fetchRoutes(
        { latitude: origin?.latitude, longitude: origin?.longitude },
        {
          latitude: destination?.latitude,
          longitude: destination?.longitude,
        }
      );
    }

    if (destination?.latitude &amp;amp;&amp;amp; origin?.latitude &amp;amp;&amp;amp; refetchNow === true) {
      triggerImmediateRefetch(false);
      fetchRoutes(
        { latitude: origin?.latitude, longitude: origin?.longitude },
        {
          latitude: destination?.latitude,
          longitude: destination?.longitude,
        }
      );
    }
  }, [
    destination?.latitude,
    origin?.latitude,
    polylineCoordinates?.length,
    refetchNow,
  ]);

  // Monitor driver location for deviation
  useEffect(() =&amp;gt; { 
    if (
      refetchLocation?.latitude &amp;amp;&amp;amp;
      destination?.latitude &amp;amp;&amp;amp;
      polylineCoordinates?.length &amp;gt; 0
    ) {
      const hasDeviated = checkDeviation(refetchLocation, polylineCoordinates);
      if (hasDeviated) {
        handleReroute(refetchLocation);
      }
    }
  }, [refetchLocation?.latitude, polylineCoordinates?.length]);

  const decodePolyline = (encoded: string): LatLng[] =&amp;gt; {
    return polyline
      .decode(encoded)
      .map((point: any) =&amp;gt; ({ latitude: point[0], longitude: point[1] }));
  };

  const returnPolyLine = () =&amp;gt; {
    if (polylineCoordinates &amp;amp;&amp;amp; polylineCoordinates.length &amp;gt; 0) {
      return (
        &amp;lt;Polyline
          coordinates={polylineCoordinates}
          strokeColor={strokeColor}
          strokeWidth={strokeWidth}
        /&amp;gt;
      );
    }
    return null;
  };

  return {
    fetchRoutes,
    returnPolyLine,
    triggerImmediateRefetch,
    triggerSetPolyline,
    justSetPolyline,
    distanceTimeObj,
  };
};

export default UseMapWithRoutes;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then reuse the Component as shown here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const {
    returnPolyLine,
    distanceTimeObj,
    justSetPolyline,
    triggerSetPolyline,
  } = UseMapWithRoutes({
    origin: mapMarkers[0],
    destination: mapMarkers[1],
    apiKey: GOOGLE_MAPS_API_KEY,
    refetchLocation: defaultLocation,
    onRouteReady: handleMapDirectionsReady,
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The returnPolyline function can then be used within your map view as in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;StyledMapView
            ref={models.mapRef}
            showsCompass={false}
            showsUserLocation={true}
            onUserLocationChange={operations.handleUserLocationChange}
            showsMyLocationButton={false}
            provider={PROVIDER_GOOGLE}
            customMapStyle={mapStyle}
            onRegionChangeComplete={operations.onregionChangeComplete}
          &amp;gt;
            {renderMapMarkers()}
            &amp;lt;MovingCarMarker
              ref={models?.carMarkerRef}
              defaultLocation={models?.defaultLocation}
            /&amp;gt;
            {operations?.returnPolyLine()}
          &amp;lt;/StyledMapView&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I really hope this helps other developers who may be having a similar challenge.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>typescript</category>
      <category>react</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Animating Custom Marker Motion On React-Native-Maps With Reanimated</title>
      <dc:creator>Victor Olufade</dc:creator>
      <pubDate>Sat, 05 Jul 2025 11:08:30 +0000</pubDate>
      <link>https://dev.to/victor_olufade_241a02b9b0/animating-custom-marker-motion-on-react-native-maps-with-reanimated-d58</link>
      <guid>https://dev.to/victor_olufade_241a02b9b0/animating-custom-marker-motion-on-react-native-maps-with-reanimated-d58</guid>
      <description>&lt;p&gt;If you have worked with React-Native in building a ride-hailing/Carpooling app or just any app that would require you to animate a marker(your custom image) on the map by coordinates, then you might have encountered the problems I did while building a carpooling app recently.&lt;/p&gt;

&lt;p&gt;My initial set-up required me to use the onUserLocationChange prop on the MapView from react-native-maps to animate the custom marker as illustrated here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { operations, models } = useTripsMapScreen({navigation, rider_id });

//In the useTripMapsScreen hook
const animateMarker = (newCoordinate: LatLng) =&amp;gt; {
  if (Platform.OS == 'android') {
    if (animatedMarkerRef.current) {
      animatedMarkerRef.current.animateMarkerToCoordinate(newCoordinate, 500);
    }
  } else {
    animatedMarkerCoord
      .timing({
        duration: 500,
        useNativeDriver: true,
        latitude: newCoordinate.latitude,
        longitude: newCoordinate.longitude,
      })
      .start();
  }
};

const handleUserLocationChange = ({
  nativeEvent: {coordinate},
}: UserLocationChangeEvent) =&amp;gt; {
  const newUserLocation = {
    coords: {
      latitude: coordinate?.latitude as number,
      longitude: coordinate?.longitude as number,
      heading: coordinate?.heading ?? 0,
    },
  };

  setUserLocation(newUserLocation);
  animateMarker({
    latitude: coordinate?.latitude,
    longitude: coordinate?.longitude,
  });
};
// end of useTripsMapScreen

&amp;lt;StyledMapView
  ref={models.mapRef}
  showsCompass={false}
  showsUserLocation={true}
  onUserLocationChange={operations.handleUserLocationChange}
  showsMyLocationButton={false}
  provider={PROVIDER_GOOGLE}
  customMapStyle={mapStyle}&amp;gt;
  {renderMapMarkers()}
  &amp;lt;Marker.Animated
    ref={models.animatedMarkerRef}
    coordinate={models.animatedMarkerCoord}&amp;gt;
    &amp;lt;Image
      source={carmaps}
      style={{
        width: 40,
        height: 40,
        transform: [{rotate: `${models.heading}deg`}],
      }}
      resizeMode="contain"
    /&amp;gt;
  &amp;lt;/Marker.Animated&amp;gt;
  &amp;lt;MapViewDirections
    origin={models.mapMarkers[0]}
    destination={models.mapMarkers[1]}
    apikey={GOOGLE_MAPS_API_KEY}
    strokeColor={theme?.colors?.screens?.mapScreen?.directionsStroke}
    strokeWidth={scale(5)}
    onReady={operations.handleMapDirectionsReady}
  /&amp;gt;
&amp;lt;/StyledMapView&amp;gt;;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This set-up worked perfectly on Android devices, but on iOS devices it behaved very strangely irrespective of whether I used the native driver or not. The marker, in my case a car image in png, would vibrate vigorously as it moved. I just could not get it to move smoothly.&lt;/p&gt;

&lt;p&gt;After so much frustration, I had to look for a solution with react-native-reanimated. Under the hood react-native-maps uses the native Animated library, but what I did was to make react-native-maps work with react-native-reanimated.&lt;/p&gt;

&lt;p&gt;Firstly, with some help from other devs, I had to create a useAnimatedRegion hook that did the actual animation using Reanimated's withTiming:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, {useCallback} from 'react';
import {MapMarker, MapMarkerProps} from 'react-native-maps';
import Animated, {
  Easing,
  EasingFunction,
  EasingFunctionFactory,
  SharedValue,
  useAnimatedProps,
  useAnimatedReaction,
  useSharedValue,
  withTiming,
} from 'react-native-reanimated';

interface LatLng {
  latitude: number;
  longitude: number;
  longitudeDelta?: number;
  latitudeDelta?: number;
}

interface AnimateOptions extends LatLng {
  duration?: number;
  easing?: EasingFunction | EasingFunctionFactory;
  rotation?: number;
  callback?: () =&amp;gt; void;
}

type MarkerProps = Omit&amp;lt;MapMarkerProps, 'coordinate'&amp;gt; &amp;amp; {
  coordinate?: MapMarkerProps['coordinate'];
};

export const AnimatedMarker = Animated.createAnimatedComponent(
  MapMarker as React.ComponentClass&amp;lt;MarkerProps&amp;gt;,
);

export const useAnimatedRegion = (
  location: Partial&amp;lt;LatLng&amp;gt; = {},
  animatedPosition?: SharedValue, 
) =&amp;gt; {
  const latitude = useSharedValue(location.latitude);
  const longitude = useSharedValue(location.longitude);
  const latitudeDelta = useSharedValue(location.latitudeDelta);
  const longitudeDelta = useSharedValue(location.longitudeDelta);
  const rotation = useSharedValue(undefined);

  const animatedProps = useAnimatedProps(() =&amp;gt; ({
    coordinate: {
      latitude: latitude.value ?? 0,
      longitude: longitude.value ?? 0,
      latitudeDelta: latitudeDelta.value ?? 0,
      longitudeDelta: longitudeDelta.value ?? 0,
      rotation: undefined,
    },
  }));

  useAnimatedReaction(
    () =&amp;gt; {
      return {
        latitude: latitude.value ?? 0,
        longitude: longitude.value ?? 0,
      };
    },
    (result, previous) =&amp;gt; {
      if (animatedPosition) {
        animatedPosition.value = result;
      }
    },
    [],
  );

  const animate = useCallback(
    (options: AnimateOptions) =&amp;gt; {
      const {duration = 500, easing = Easing.linear} = options;

      const animateValue = (
        value: SharedValue&amp;lt;number | undefined&amp;gt;,
        toValue?: number,
        callback?: () =&amp;gt; void,
      ) =&amp;gt; {
        if (!toValue) {
          return;
        }

        value.value = withTiming(
          toValue,
          {
            duration,
            easing,
          },
          callback,
        );
      };

      animateValue(latitude, options.latitude);
      animateValue(longitude, options.longitude, options.callback);
      animateValue(latitudeDelta, options?.latitudeDelta);
      animateValue(longitudeDelta, options?.longitudeDelta);
      //@ts-ignore
      animateValue(rotation, options?.rotation);
    },
    [latitude, longitude, latitudeDelta, longitudeDelta, rotation],
  );

  return {
    props: animatedProps,
    animate,
  };
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next I had to create the actual marker component that took a ref and used the useImperativeHandle hook from react to call the animate function on my png image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { forwardRef, useImperativeHandle, useRef } from "react";
import { SharedValue } from "react-native-reanimated";
import { AnimatedMarker, useAnimatedRegion } from "./useAnimatedMarker";
import { LatLng } from "react-native-maps";
import { LATITUDE_DELTA, LONGITUDE_DELTA } from "@env";
import { Image } from "react-native";

const carmaps = require("../../../assets/images/carmaps.png");

export interface MovingCarMarkerProps {
  defaultLocation: {
    latitude: number;
    longitude: number;
    heading: number;
  }; 
  heading?: number;
  animatedPosition?: SharedValue;
}

export interface MovingCarMarker {
  animateCarToPosition: (
    newCoords: LatLng,
    speed?: number,
    callback?: () =&amp;gt; void
  ) =&amp;gt; void;
}

export const MovingCarMarker = forwardRef(
  (props: MovingCarMarkerProps, ref) =&amp;gt; {
    const defaultCarRef = useRef({
      latitude: props?.defaultLocation?.latitude,
      longitude: props?.defaultLocation?.longitude,
      latitudeDelta: LATITUDE_DELTA,
      longitudeDelta: LONGITUDE_DELTA,
    });

    const animatedRegion = useAnimatedRegion(
      {
        latitude: parseFloat(
          defaultCarRef?.current?.latitude
            ? defaultCarRef?.current?.latitude?.toString()
            : "0"
        ),
        longitude: parseFloat(
          defaultCarRef?.current?.longitude
            ? defaultCarRef?.current?.longitude?.toString()
            : "0"
        ),
      },
      props?.animatedPosition
    );

    useImperativeHandle(ref, () =&amp;gt; ({
      animateCarToPosition: (
        newCoords: LatLng,
        speed?: number, 
        callback?: () =&amp;gt; void
      ) =&amp;gt; {
        animatedRegion.animate({
          latitude: newCoords?.latitude,
          longitude: newCoords?.longitude,
          duration: speed || 500,
          callback,
        });
      },
    }));

    return (
      &amp;lt;AnimatedMarker
        zIndex={20}
        anchor={{ x: 0.5, y: 0.5 }}
        rotation={props?.defaultLocation?.heading}
        animatedProps={animatedRegion.props}
      &amp;gt;
        &amp;lt;Image
          source={carmaps}
          style={{
            width: 40,
            height: 40
          }}
          resizeMode="contain"
        /&amp;gt;
      &amp;lt;/AnimatedMarker&amp;gt;
    );
  }
);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once I had these two set up, all I had to do was to use my MovingCarMarker component within the styled MapView from react-native-maps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// in my custom hook
const carMarkerRef = useRef&amp;lt;MovingCarMarker | null&amp;gt;(null);

const handleUserLocationChange = ({
  nativeEvent: {coordinate},
}: UserLocationChangeEvent) =&amp;gt; {
  const newUserLocation = {
    coords: {
      latitude: coordinate?.latitude as number,
      longitude: coordinate?.longitude as number,
      heading: coordinate?.heading ?? 0,
    },
  };

  setUserLocation(newUserLocation);

  carMarkerRef?.current?.animateCarToPosition({
    latitude: newUserLocation?.coords?.latitude,
    longitude: newUserLocation?.coords?.longitude,
  });
};
// end of custom hook

&amp;lt;StyledMapView
  ref={models.mapRef}
  showsCompass={false}
  showsUserLocation={true}
  onUserLocationChange={operations.handleUserLocationChange}
  showsMyLocationButton={false}
  provider={PROVIDER_GOOGLE}
  customMapStyle={mapStyle}
  onRegionChangeComplete={operations.onregionChangeComplete}&amp;gt;
  {renderMapMarkers()}
  &amp;lt;MovingCarMarker
    ref={models?.carMarkerRef}
    defaultLocation={models?.defaultLocation}
  /&amp;gt;
  {operations?.returnPolyLine()}
&amp;lt;/StyledMapView&amp;gt;;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that in the handleUserLocationChange function, I not only use the ref to animate to a new position, I also update the models?.defaultLocation state been passed to the MovingCarMarker as defaultLocation with the new coordinates.&lt;/p&gt;

&lt;p&gt;With this set-up your custom marker would animate very smoothly between coordinates in your react-native app. Please let me know if this helps you in any way. Questions and contributions are also very welcome. &lt;/p&gt;

&lt;p&gt;Even with this solution, there's still a lot of room for improvement, so feel free to customize the implementation as you deem fit. &lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>mobile</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
