📜 ⬆️ ⬇️

Creating a React-Hook usePosition () to get and track browser coordinates

image


In short


In this article, we will create a usePosition() React hook to track browser geolocation. Under the hood, this hook will use the getCurrentPosition() and watchPosition() methods of the native browser object navigator.geolocation . I published the final version of the hook on GitHub and NPM .


Why create the usePosition() hook in principle usePosition()


One of the important advantages of hooks in React is the ability to isolate logically related code fragments in one place (in a hook), while avoiding the need to mix logically unrelated code fragments, for example, in the component componentDidMount() method.


Suppose we want to get the coordinates of the browser ( latitude and longitude ) and after receiving the coordinates request the weather forecast or the current temperature in this region from a third-party service. The code for these two functionalities (getting coordinates and querying temperature) in React is often placed inside the same componentDidMount() method. In this case, the componentWillUnmount() method is usually "cleaned up" behind itself, calling the clearWatch () method to stop tracking the location of the browser. Such an approach increases the size of the methods, breaks logically related parts of the code into parts (separate subscription and unsubscribe from spying on the location of the browser) and combines logically weakly connected parts of the code into one method (obtaining coordinates and temperatures). Reading the code is difficult, as is its debugging and support.


Next, we will try to bring the functionality related to getting the coordinates of the browser into a separate usePosition() hook, in order to avoid the difficulties listed above.


How we will use the hook usePosition()


Let's go "by contradiction" and before the implementation of the hook itself, let's plan how we will use it. This will help us decide on the hook interface (what it will receive and what to return).


The simplest example of obtaining coordinates and displaying them on the screen might look like this:


 import React from 'react'; //    . import { usePosition } from './usePosition'; export const UsePositionDemo = () => { //    ( ) . const { latitude, longitude, error } = usePosition(); //       (   ). return ( <> latitude: {latitude}, longitude: {longitude}, error: {error} </> ); }; 

Just one line with the hook usePosition() and we can operate with the coordinates latitude and longitude . We did not even use the useState() or useEffect() hooks explicitly here. A subscription to tracking coordinates, as well as deleting a subscriber is encapsulated into one usePosition() hook. Then the magic of redrawing the components of React will do everything for us. At the beginning, the coordinates will be null or undefined . As soon as the coordinates are received, the component will be redrawn and we will see them on the screen.


Implement hook usePosition()


Our usePosition() hook is a regular JavaScript function that looks like this:


 //  . export const usePosition = () => { //    . } 

We will use the useState () hooks for internal storage of coordinates and useEffect () for subscription and unsubscribe from tracking coordinates. For this we have to import them.


 import { useState, useEffect } from 'react'; export const usePosition = () => { //     . } 

Create state variables in which we will store coordinates or an error in obtaining coordinates (for example, if the user refuses to share his location).


 import { useState, useEffect } from 'react'; export const usePosition = () => { const [position, setPosition] = useState({}); const [error, setError] = useState(null); //  ... } 

Also at this stage we can return the variables that are expected from the hook. So far there is nothing useful in these variables, but we will fix it soon.


 import { useState, useEffect } from 'react'; export const usePosition = () => { const [position, setPosition] = useState({}); const [error, setError] = useState(null); // other code goes here... return { ...position, error }; } 

And now the key moment of implementation is getting the coordinates.


 import { useState, useEffect } from 'react'; export const usePosition = () => { const [position, setPosition] = useState({}); const [error, setError] = useState(null); //     ,    .. useEffect(() => { const geo = navigator.geolocation; if (!geo) { setError('   '); return; } //     . watcher = geo.watchPosition(onChange, onError); //  ,       //    ,    . return () => geo.clearWatch(watcher); }, []); return {...position, error}; } 

In the useEffect() hook, we first check to see if the browser supports coordinate detection. If the functionality is not supported, we exit the hook with an error. Otherwise, we subscribe to changes in the browser's location using the onChange onChange() and onError() (we will add their code below). Please note that from the useEffect() hook we return an anonymous function that will be executed if the component is removed from the display. In this anonymous function, we unsubscribe from spying so as not to clutter up the memory. Thus, the whole logic of subscription and unsubscribe from tracking is in the same hook usePosition() .


Let's add the missing callbacks:


 import { useState, useEffect } from 'react'; export const usePosition = () => { const [position, setPosition] = useState({}); const [error, setError] = useState(null); const onChange = ({latitude, longitude}) => { //        position,   //    ,    . setPosition({latitude, longitude}); }; const onError = (error) => { setError(error.message); }; useEffect(() => { const geo = navigator.geolocation; if (!geo) { setError('   '); return; } //     . watcher = geo.watchPosition(onChange, onError); //  ,       //    ,    . return () => geo.clearWatch(watcher); }, []); return {...position, error}; } 

The usePosition() hook is ready to use.


At last


You can find a demonstration of the work of the hook and its more detailed implementation with the ability to set tracking parameters on GitHub .


I hope this article has been helpful to you. Successful coding!


')

Source: https://habr.com/ru/post/458614/


All Articles