Best approach to extract repeated code from screens using hooks (setState, useEffect)?

Viewed 438

The question may seem a little vague, I'm new using hooks, I'll be quite specific in my example, I have 3 variables, with their setter, and a useEffect that works on them. The code basically asks the user for location permissions and saves his position.

This piece of code is reused exactly the same in two different screens, my question is, to what extent it is feasible to move all the code variables and setters, and use effect to a third file "helper".

Here is the piece of code:

  const [localitzacioActual, setlocalitzacioActual] = useState(null);
  const [localitzacioPermisos, setlocalitzacioPermisos] = useState(null);
  const [mapRegion, setMapRegion] = useState(null);

  useEffect( () => {
    const demanarPermisos = async () => {
      let { status } = await Permissions.askAsync(Permissions.LOCATION);
      if (status !== 'granted') {
        setlocalitzacioPermisos('Permisos denegats')
      } else {
        setlocalitzacioPermisos(true)
      }
      let location = await Location.getCurrentPositionAsync({});
      setlocalitzacioActual(JSON.stringify(location))
      setMapRegion({ latitude: location.coords.latitude, longitude: location.coords.longitude, latitudeDelta: 0.0022, longitudeDelta: 0.0121 });
    }
    demanarPermisos()
  }, []);

To what point I can instantiate this code to another file, y still need to declare the constants, and the use effect but I can move all the login to a third function outside of the file?

Thanks!

2 Answers

You can put all of your state variables and the function in a custom hook. Your custom hook will handle the state changes for you.

permisos.js

import { useState } from 'react';

const usePermisos= () => {
  const [localitzacioActual, setlocalitzacioActual] = useState(null);
  const [localitzacioPermisos, setlocalitzacioPermisos] = useState(null);
  const [mapRegion, setMapRegion] = useState(null);

  const demanarPermisos = async () => {
    let { status } = await Permissions.askAsync(Permissions.LOCATION);
    if (status !== 'granted') {
      setlocalitzacioPermisos('Permisos denegats')
    } else {
      setlocalitzacioPermisos(true)
    }
    let location = await Location.getCurrentPositionAsync({});
    setlocalitzacioActual(JSON.stringify(location))
    setMapRegion({ latitude: location.coords.latitude, longitude: location.coords.longitude, latitudeDelta: 0.0022, longitudeDelta: 0.0121 });
  };

  return [
    localitzacioActual,
    localitzacioPermisos,
    mapRegion,
    demanarPermisos,
  ];
};

export default usePermisos;

Then import them wherever you need them. You still have to use useEffect to fire off your function.

screen1.js

import React, { useEffect } from 'react';
import usePermisos from './usePermisos';

const screen1 = () => {
  const [
    localitzacioActual,
    localitzacioPermisos,
    mapRegion,
    demanarPermisos,
  ] = usePermisos();

  useEffect(demanarPermisos, []);

  return (
    <div>React Functional Component</div>
  );
};

export default screen1;

If you need your setters outside of demanarPermisos you can return them from usePermisos.

Well, I'll answer my own question. For anyone wondering the same thing:

Yes, it is possible to move all the code out to a third function. Just add a return with all the variables you need in the screen:

LocalitzacioHelper.js

import React, {useState, useEffect} from 'react';
import * as Location from 'expo-location';
import * as Permissions from 'expo-permissions';

export const demanarLocalitzacio = () => {
  const [localitzacioActual, setlocalitzacioActual] = useState(null);
  const [localitzacioPermisos, setlocalitzacioPermisos] = useState(null);
  const [mapRegion, setMapRegion] = useState(null);

  useEffect( () => {
    const demanarPermisos = async () => {
      let { status } = await Permissions.askAsync(Permissions.LOCATION);
      if (status !== 'granted') {
        setlocalitzacioPermisos('Permisos denegats')
      } else {
        setlocalitzacioPermisos(true)
      }
      let location = await Location.getCurrentPositionAsync({});
      setlocalitzacioActual(JSON.stringify(location))
      setMapRegion({ latitude: location.coords.latitude, longitude: location.coords.longitude, latitudeDelta: 0.0022, longitudeDelta: 0.0121 });
    }
    demanarPermisos()
  }, []);

  return [localitzacioActual, localitzacioPermisos, mapRegion]
}

Then in the screen you just call the function before the return:

MapaScreen.js

const [localitzacioActual, localitzacioPermisos, mapRegion] = demanarLocalitzacio()

The use effect will have the exact same behavior as it was directly inside de screen render function.

Related