map of the world with a 'you are here' pointer, pointing randomly to somewhere in africa

Creating A Higher Order Component In React To Capture Browser Location Settings
Published: 13-Oct-2019

Getting the browser location is useful for when you need to pass latitude, longitude, altitude, speed and similar data to your app.

MDN tells us that getting the user location can be done using the Navigator interface of the browser. A basic example from MDN is:


  navigator.geolocation.getCurrentPosition(function(position) {
    let lat = position.coords.latitude;
    let long = position.coords.longitude;

    latText.innerText = lat.toFixed(2);
    longText.innerText = long.toFixed(2);
  });


That's all well and good, but how do we do this in React?

As with anything in React, there is always more than one way to do 'the thing'. We're going to create a higher order component to do 'the thing' in the following example. In our use-case, we only need to get the user location once, and the app will assume the user does not move an appreciable distance from their original location when initialising and using the app in this way.

What is a higher order component?

I'm glad you asked. I think of a higher order component as a component that passes props to subordinate components (the component(s) it wraps).

Here's some example code for doing the thing:

import React from 'react';
import { getMyLocation } from '../utilities';
const withLocation = (WrappedComponent) => (
class extends React.Component {
constructor(props) {
super(props);
this.state = {
accuracy: null,
altitude: null,
altitudeAccuracy: null,
error: {},
heading: null,
latitude: null,
longitude: null,
speed: null,
timestamp: null,
withLocation: {
complete: false,
},
};
}
async componentDidMount() {
await getMyLocation()
.then((location) => {
this.setState((prevState) => (
{
...prevState,
...location,
withLocation: {
complete: location.error.code === 0,
},
}
));
});
}
render() {
const {
error,
latitude,
longitude,
withLocation: internalChecks,
} = this.state;
const location = {
error,
latitude,
longitude,
};
return internalChecks.complete ? <WrappedComponent location={location} {...this.props} /> : null;
}
});
export default withLocation;

Let's take a look at how it works

This functional component only imports React and a helper utility. We will look at the helper utility in the next article.

The functional component 'withLocation' is a function which returns a class component. A component (called WrappedComponent) is passed to the class as an argument of withLocation.

The main work is performed by a class in this case because we want to take advantage of componentDidMount (or we could use useEffect as a hook in a functional component).

The constructor provides us props and state. In this case, I've provided most of the items that could be returned by the window.navigator object for illustration purposes. You don't have to use all of these items in your own app.

The componentDidMount lifecycle method is initalised with an async statement. This is because we need to wait for the user to give permission to provide access to their location (this is a standard browser security feature in case you weren't aware). As mentioned above, we'll talk about getLocation in the next article.

Once location is returned, the next step is to update the state using this.setState. The withLocation portion uses a ternary shorthand to produce true or false of whether the user chose to provide access to their location or not.

The next step is the render method, where we access the pieces of the state needed by the rest of the app. In this case we're grabbing the error property, the coordinates (latitude and longitude properties) and the withLocation property (which we're exposing as 'internalChecks').

After that the code creates a location object which is populated by error and latitude and longitude. This object in turn will be passed as a property of the WrappedComponent object.

You'll notice that the internalChecks.complete boolean is used to either return the WrappedComponent with the location prop and any other props available, or, to return null.

In practice you will probably want to return another component rather than null. The reason for this is, returning null will not inform the user as to why the app is not working. A component that returns an error message or some information about the user's choice is probably a more desirable option than null. This really depends on the use case you have for this component in your own app though.

And that's it. This is how you can create a higher order component to return current location props to your app!

map of the world with a 'you are here' pointer, pointing randomly to somewhere in africa

Creating A Higher Order Component In React To Capture Browser Location Settings
Published: 13-Oct-2019

Getting the browser location is useful for when you need to pass latitude, longitude, altitude, speed and similar data to your app.

MDN tells us that getting the user location can be done using the Navigator interface of the browser. A basic example from MDN is:


  navigator.geolocation.getCurrentPosition(function(position) {
    let lat = position.coords.latitude;
    let long = position.coords.longitude;

    latText.innerText = lat.toFixed(2);
    longText.innerText = long.toFixed(2);
  });


That's all well and good, but how do we do this in React?

As with anything in React, there is always more than one way to do 'the thing'. We're going to create a higher order component to do 'the thing' in the following example. In our use-case, we only need to get the user location once, and the app will assume the user does not move an appreciable distance from their original location when initialising and using the app in this way.

What is a higher order component?

I'm glad you asked. I think of a higher order component as a component that passes props to subordinate components (the component(s) it wraps).

Here's some example code for doing the thing:

import React from 'react';
import { getMyLocation } from '../utilities';
const withLocation = (WrappedComponent) => (
class extends React.Component {
constructor(props) {
super(props);
this.state = {
accuracy: null,
altitude: null,
altitudeAccuracy: null,
error: {},
heading: null,
latitude: null,
longitude: null,
speed: null,
timestamp: null,
withLocation: {
complete: false,
},
};
}
async componentDidMount() {
await getMyLocation()
.then((location) => {
this.setState((prevState) => (
{
...prevState,
...location,
withLocation: {
complete: location.error.code === 0,
},
}
));
});
}
render() {
const {
error,
latitude,
longitude,
withLocation: internalChecks,
} = this.state;
const location = {
error,
latitude,
longitude,
};
return internalChecks.complete ? <WrappedComponent location={location} {...this.props} /> : null;
}
});
export default withLocation;

Let's take a look at how it works

This functional component only imports React and a helper utility. We will look at the helper utility in the next article.

The functional component 'withLocation' is a function which returns a class component. A component (called WrappedComponent) is passed to the class as an argument of withLocation.

The main work is performed by a class in this case because we want to take advantage of componentDidMount (or we could use useEffect as a hook in a functional component).

The constructor provides us props and state. In this case, I've provided most of the items that could be returned by the window.navigator object for illustration purposes. You don't have to use all of these items in your own app.

The componentDidMount lifecycle method is initalised with an async statement. This is because we need to wait for the user to give permission to provide access to their location (this is a standard browser security feature in case you weren't aware). As mentioned above, we'll talk about getLocation in the next article.

Once location is returned, the next step is to update the state using this.setState. The withLocation portion uses a ternary shorthand to produce true or false of whether the user chose to provide access to their location or not.

The next step is the render method, where we access the pieces of the state needed by the rest of the app. In this case we're grabbing the error property, the coordinates (latitude and longitude properties) and the withLocation property (which we're exposing as 'internalChecks').

After that the code creates a location object which is populated by error and latitude and longitude. This object in turn will be passed as a property of the WrappedComponent object.

You'll notice that the internalChecks.complete boolean is used to either return the WrappedComponent with the location prop and any other props available, or, to return null.

In practice you will probably want to return another component rather than null. The reason for this is, returning null will not inform the user as to why the app is not working. A component that returns an error message or some information about the user's choice is probably a more desirable option than null. This really depends on the use case you have for this component in your own app though.

And that's it. This is how you can create a higher order component to return current location props to your app!