Clone Experiment #3: Accessible Taxi Booking app for web and mobile
Tags: react, reactnative, experiment, clone
Published: 2022-02-18 12:15:17

**Introduction**

We have created a cross-platform Taxi services application using NativeBase. The main objective for this project was to show users how a single codebase having NativeBase components can be deployed as a web and a native app. The application created is inspired by the leading Taxi Service provider [Uber’s](https://www.uber.com/in/en/) application. The experiment’s final result looked like this. [Check it out yourself.](https://nativebase-v3-taxi-app.vercel.app/)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cyslz622wxei5huoc4c8.gif)

**Motivation:**

Why should we build native apps when we have responsiveness in web apps?

Better user experience and cross-platform consistency are possible only through Native Apps.

Finding a solution for this, I came across the Expo framework which helps you to develop, build, deploy, and quickly iterate on iOS, Android, and Web apps from the same JavaScript / TypeScript codebase. But again, designing & achieving responsiveness is very complex in Expo / React Native. 

Moreover, unlike CSS, there is nothing similar to media queries in React Native. You will end up writing separate style objects and individual files to support responsiveness in your App. That is where NativeBase comes to the rescue. It supports responsive styles out of the box. You can achieve responsiveness by using either an [Object](https://docs.nativebase.io/responsive#h2-the-object-syntax) or [Array](https://docs.nativebase.io/responsive#h2-the-array-syntax) syntax in the props.

**Challenges faced:**

- Maps: The major challenge we have faced was the absence of a map API that works collectively on both native and web. For that, we needed to call different APIs based on the device.

```javascript
import { Platform } from "react-native";

const ResponsiveMap = Platform.select({
  native: () => (
    <NativeMap />
  ),
  default: () => <WebMap />,
});
```

NativeMap.tsx

```javascript
import React from "react";
import MapView, { PROVIDER_GOOGLE } from "react-native-maps";

function NativeMap(props: any) {
  return (
    <MapView
      style={{
        flex: 1,
        minHeight: 120,
      }}
      provider={PROVIDER_GOOGLE}
      region={{
        latitudeDelta: 0.015,
        longitudeDelta: 0.0121,
        latitude: 12.9698,
        longitude: 77.75,
      }}
    >
      <MapCircle
        center={{ latitude: 12.9698, longitude: 77.75 }}
        radius={100}
      ></MapCircle>
    </MapView>
  );
}
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8423emnnuxny2lpxqs5q.png)

WebMap.tsx

```javascript
import React, { useEffect, useState, useRef } from "react";
import { VStack, View } from "native-base";
import Constants from "expo-constants";

function WebMap() {
  const [mapLoaded, setMapLoaded] = useState(false);
  const mapContainerRef = useRef<HTMLElement>(null);    // C1

  useEffect(() => {
    // Check if map script is already present in DOM
    if (!document.body.dataset.mapLoaded) {
      const mapScript = document.createElement("script");
      mapScript.src = MAP_SCRIPT_WITH_API_KEY;

      mapScript.onload = () => {
        // set dataset property on body to indicate map script has been loaded.
        document.body.dataset.mapLoaded = "true";
        setMapLoaded(true);
      };
      document.head.appendChild(mapScript);
    }
  }, []);

  useEffect(() => {
    if (mapLoaded) {
      //@ts-ignore
      const map = new window.google.maps.Map(mapContainerRef.current, {
        zoom: 15,
        mapTypeId: "terrain",
        center: { lat: 12.9698, lng: 77.75 },
      });
    }
  }, [mapLoaded]);

  return (
    <>
      {mapLoaded ? (
        <VStack
          safeAreaBottom
          flex="1"
          space={{ base: "4", md: "0" }}
          rounded={{ md: "lg" }}
          borderWidth={{ base: "0", md: "1" }}
          _light={{
            bg: { md: "white", base: "primary.50" },
            borderColor: "coolGray.200",
          }}
          _dark={{
            bg: { base: "coolGray.700", md: "coolGray.700" },
            borderColor: "coolGray.700",
          }}
        >
          <View flex="1" ref={mapContainerRef} />
        </VStack>
      ) : (
        "Loading ..."
      )}
    </>
  );
}
```

As you can see, in the line C1,  we need to initialise the container as HTMLElement to show the map in web.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2cpi8us1phswpy4gmdqg.png)

- Sidebar: We needed to show the Sidebar differently on small and large-sized screens.

```javascript
const isLargeScreen = useBreakpointValue({
    base: false,
    lg: true,
  });

return(
...

{isLargeScreen ? (
    <Box w="300" bg="white" display={isSidebar ? "flex" : "none"}>
      <Sidebar navigation={navigation} />
    </Box>
) : (
    <Slide in={isSlideOpen} placement="left" w={width} h="100">
    <HStack w="100%" h="100%">
      <Box w={{ base: "80%", lg: "25%" }} bg="white">
        <Sidebar navigation={navigation} />
      </Box>
      <Pressable
        w={{ base: "20%", lg: "75%" }}
        onPress={() => setSlideOpen(false)}
        opacity="0.5"
        bg="black"
      ></Pressable>
    </HStack>
  </Slide>
)}
...
);
```

Small screens


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1v1wmu01ceckjcwoo7qs.png)

Large screens


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/noctsskoexn364sa8e6x.png)

**Conclusion**

With this fun experiment, we were able to show what NativeBase components can do with the expo. It is amazing how our code gets converted to div or React Native components on the basis of the device on which it gets rendered. The support of props has also allowed us to write minimal code to support the same design on the web as well as mobile. 

We are working to enhance NativeBase components by turning the challenges faced in this experiment into features.