dannym
dannym

Reputation: 95

How to use variables from exported constants in other files?

I have this exported const in one file useLocation.tsx where I get the user's location and retrieve the user's county, state/province, and country. I also have an exported const in another file useCountryData.tsx where I fetch the COVID cases and deaths from an API. There is a variable in useLocation.tsx that is called countryNameshort. How do I use this variable in useCountryData.tsx?

useLocation.tsx

export const useLocation = () => {  
  
    var [stateName, setstateName] = useState(String);
    var [countyName, setCountyName] = useState(String);
    var [countryName, setCountryName] = useState(String);
    var [stateNameshort, setstateNameshort] = useState(String);
    var [countryNameshort, setCountryNameshort] = useState(String);
  
  
  
    const [latitude, setlatitude] = useState(Number);
    const [longitude, setlongitude] = useState(Number);
  
    const [location, setLocation] = useState(Object);
    const [errorMsg, setErrorMsg] = useState(String);
      
    useEffect(() => {
          (async () => {
            if (Platform.OS === "android" && !Constants.isDevice) {
              setErrorMsg(
                "Oops, this will not work on Snack in an Android emulator. Try it on your device!"
              );
              return;
            }
            let { status } = await Location.requestPermissionsAsync();
            if (status !== "granted") {
              setErrorMsg("Permission to access location was denied");
              return;
            }
      
            let location = await Location.getCurrentPositionAsync({});
            setLocation(location);
      
            const latitude = location.coords.latitude;
            setlatitude(latitude);
            const longitude = location.coords.longitude;
            setlongitude(longitude);
          })();
        }, []);
      
        let text = "Waiting..";
        if (errorMsg) {
          text = errorMsg;
        } else if (location) {
          text = JSON.stringify(location);
        }
      
    fetch(
          "https://maps.googleapis.com/maps/api/geocode/json?address=" +
            latitude +
            "," +
            longitude +
            "&key=" +
            apiKey
        )
          .then((response) => response.json())
          .then((responseJson) => {
            const resState = responseJson.results[0].address_components.filter(
              (x: any) =>
                x.types.filter((t: Object) => t == "administrative_area_level_1")
                  .length > 0
            )[0].long_name;
            setstateName(resState);
            const resCounty = responseJson.results[0].address_components.filter(
              (x: any) =>
                x.types.filter((t: Object) => t == "administrative_area_level_2")
                  .length > 0
            )[0].long_name;
            setCountyName(resCounty);
            const resCountry = responseJson.results[0].address_components.filter(
              (x: any) => x.types.filter((t: Object) => t == "country").length > 0
            )[0].long_name;
            setCountryName(resCountry);
            const resStateShort = responseJson.results[0].address_components.filter(
              (x: any) =>
                x.types.filter((t: Object) => t == "administrative_area_level_1")
                  .length > 0
            )[0].short_name;
            setstateNameshort(resStateShort);
            const resCountryShort = responseJson.results[0].address_components.filter(
              (x: any) => x.types.filter((t: Object) => t == "country").length > 0
            )[0].short_name;
            setCountryNameshort(resCountryShort);
            if (countryNameshort === "US") {
              countryNameshort = "US" + "A";
            }
          })
          .catch((err) => {
            console.log(err);
          });

        return { countryName, countyName, stateName, stateNameshort, countryNameshort };
      };

useCountryData.tsx

import { useLocation } from './useLocation';

export const useCountryData = () => {
  const [earliest2, setEarliest2] = useState([]);
  const [countryDeaths, setcountryDeaths] = useState(Number);
  const [countryCases, setcountryCases] = useState(Number);

  useEffect(() => {
    axios
      .get("https://coronavirus-19-api.herokuapp.com/countries")
      .then((response) => {
        setEarliest2(response.data);

        const countryArray = response.data.filter(
          (item) => item.country === props.countryNameshort //???
        );

        const resCountryDeaths = countryArray[0].deaths;
        setcountryDeaths(resCountryDeaths);

        const resCountryCases = countryArray[0].cases;
        setcountryCases(resCountryCases);
        console.log("hiiii", countryCases);
      })
      .catch((err) => {
        console.log(err);
      });
  }, []);

  return { countryCases, countryDeaths };
};

CountryCard.tsx

const CountryCard = (props) => {
  const mappedLocation = useMappedLocation();
  const countryName = mappedLocation.country;

  return (
    <RectButton style={[styles.container, { backgroundColor: "white" }]}>
      <Text style={[styles.textLocation, { top: 15, left: 10 }]}>
        {countryName} /???
      </Text>
)
}

Upvotes: 2

Views: 1230

Answers (1)

cefn
cefn

Reputation: 3341

Here is a pseudo-code suggestion for how you could refactor these stages, without adopting useEffect and useState for operations that are more conventionally just async, followed with a 'hook-style' pattern which does useState and useEffect to make the async results available to your UI. There is no chance on earth that this code will run as I don't have access to your environment to really try it, but it gives you an idea of how it might be refactored. If the state needs to be consumed by multiple parts of your UI, then makes sense for the useMappedLocation hook to assign a mappedLocation variable in an ancestor component, with the result passed down to descendants through Context, Composition or Props. This will have the effect of caching the result.

I've also sketched out how a second hook might consume the first hook as I think having re-read your question that was the point you got stuck with your original approach. However, embedding the useMappedLocation hook in multiple places will cause it to be re-executed multiple times and will not benefit from caching compared to hoisting it into an ancestor component.

const apikey = "myapikey";

interface GeoEntry {
  address_components:[
    {types:("country"|"administrative_area_level_1")[]
      short_name:string,
      long_name:string
    }
  ]
}

interface MappedLocation {
  state:string,
  country:string
}

async function getLocation(){
  return await Location.getCurrentPositionAsync({});
}

async function getFirstGeoEntry() : Promise<GeoEntry>{
  const {latitude,longitude} = await getLocation();
  const response = await fetch(
          "https://maps.googleapis.com/maps/api/geocode/json?address=" +
            latitude +
            "," +
            longitude +
            "&key=" +
            apikey
        )
  const json = await response.json();
  return json.results[0]
}

function getStateNameLong(geoEntry:GeoEntry){
  return geoEntry.address_components.filter(
              (x: any) =>
                x.types.filter((t: Object) => t == "administrative_area_level_1")
                  .length > 0
            )[0].long_name
}

function getCountryNameShort(geoEntry:GeoEntry){
  return geoEntry.address_components.filter(
              (x: any) => x.types.filter((t: Object) => t == "country").length > 0
            )[0].short_name
} 

async function getMappedLocation() : Promise<MappedLocation>{
  const geoEntry = await getFirstGeoEntry();
  return {
    country:getCountryNameShort(geoEntry),
    state:getStateNameLong(geoEntry),
  }
}

const useMappedLocation = () => {
  const [mappedLocation,setMappedLocation] = useState<MappedLocation>(null);
  useEffect(() => {
    (async () => {
      setMappedLocation(await getMappedLocation())
    })()
  }, [])
  return mappedLocation
}

Here's how a second hook ( useCountryData ) might consume the first ( useMappedLocation ). Note the useEffect handles the case that the location hasn't arrived yet, and mappedLocation is in the dependency array to ensure the useEffect runs a second time when the mappedLocation DOES finally arrive.

import { useMappedLocation } from './useMappedLocation';

export const useCountryData = () => {
  const [earliest2, setEarliest2] = useState([]);
  const [countryDeaths, setcountryDeaths] = useState(Number);
  const [countryCases, setcountryCases] = useState(Number);

  const mappedLocation = useMappedLocation()

  useEffect(() => {
    if(mappedLocation !== null){
      axios.get("https://coronavirus-19-api.herokuapp.com/countries")
        .then((response) => {
          setEarliest2(response.data);

          const countryArray = response.data.filter(
            (item) => item.country === mappedLocation.country
          );

          const resCountryDeaths = countryArray[0].deaths;
          setcountryDeaths(resCountryDeaths);

          const resCountryCases = countryArray[0].cases;
          setcountryCases(resCountryCases);
          console.log("hiiii", countryCases);
        })
        .catch((err) => {
          console.log(err);
        });
    }
  }, [mappedLocation]);

  return { countryCases, countryDeaths };
};

Upvotes: 1

Related Questions