1

Hope you can help me with this. So I am trying to use IP address to get latitude and longitude and therefore get a location on a map when an new IP is submitted using axios and react-query but can't find out what am I doing wrong.
I tried with only useState and then setting setIp(data); to handleSubmit but it didn't work as well. I always get undefined data when refresh page so I handled that like this { geoData && <CreateMap ...

Is it even right approach with react-query? Is this how I fetch the data? Thank you for your help!

I have 2 components called Input and CreateMap.

Input.jsx:

import React, { useState } from "react";
import axios from "axios";
import CreateMap from "./CreateMap";
import { useQuery } from "@tanstack/react-query";

const InputComponent = () => {
  const [ip, setIp] = useState("8.8.8.8");
  const [geoData, setGeoData] = useState();

  const { isLoading, isError, data, error } = useQuery({
    queryKey: ["posts"],
    queryFn: async () => {
      try {
        const response = await axios.get(
          `https://api.ipgeolocation.io/ipgeo?apiKey=e1ba80b5c7a64f0fb61db1f286d78562&ip=${ip}`
        );
        return response.data;
      } catch (error) {
        throw new Error(error);
      }
    },
  });

  if (isLoading) {
    return <h1>Loading...</h1>;
  }
  if (isError) {
    return <h1>Error: {error.message}</h1>;
  }

  const handleIpChange = (e) => {
    setIp(e.target.value);
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    setGeoData(data)
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>Write your IP address: </label>
      <input type="text" defaultValue={ip} onChange={handleIpChange} />
      <button type="submit">Submit</button>
      { geoData && <CreateMap lat={geoData.latitude} lng={geoData.longitude} /> }
    </form>
  );
};

export default InputComponent;

CreateMap.jsx

import React from "react";
import { MapContainer, Marker, Popup, TileLayer } from "react-leaflet";
import { Icon } from "leaflet";

import "./Map.css";


const CreateMap = ({ lat, lng }) => {
 
  return (
    <div>
        <MapContainer
          style={{ height: "480px", width: "100%" }}
          center={[lat, lng]}
          zoom={12}
        >
          <TileLayer
            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
            attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          />
        </MapContainer>
    </div>

  );
};

export default CreateMap;

and App.js

import React from 'react'
import Input from './components/Input'


const App = () => {

  return (
    <div>
      <h1>App</h1>
      <Input />
    </div>
  )
}

export default App
dean89
  • 111
  • 7
  • I think you should refetch manually. Check these answers: https://stackoverflow.com/questions/62340697/react-query-how-to-usequery-when-button-is-clicked – OneQ May 13 '23 at 16:29

1 Answers1

2

Something like this should be good with the refetch from react-query

const InputComponent = () => {
  const [ip, setIp] = useState("8.8.8.8");

  const { isLoading, isError, data, error, refetch } = useQuery({
    queryKey: ["posts"],
    queryFn: async () => {
      try {
        const response = await axios.get(
          `https://api.ipgeolocation.io/ipgeo?apiKey=e1ba80b5c7a64f0fb61db1f286d78562&ip=${ip}`
        );
        return response.data;
      } catch (error) {
        throw new Error(error);
      }
    },
    enabled: false
  });

  if (isLoading) {
    return <h1>Loading...</h1>;
  }
  if (isError) {
    return <h1>Error: {error.message}</h1>;
  }

  const handleIpChange = (e) => {
    setIp(e.target.value);
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    refetch();
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>Write your IP address: </label>
      <input type="text" defaultValue={ip} onChange={handleIpChange} />
      <button type="submit">Submit</button>
      { data && <CreateMap lat={data.latitude} lng={data.longitude} /> }
    </form>
  );
};

For the rerender, create a control component :

function UpdateCenter({ center }) {
  const map = useMap();
  map.setView(center);
  return null;
}

And add it in your CreateMap :

const CreateMap = ({ lat, lng }) => {
  return (
    <div>
        <MapContainer
          style={{ height: "480px", width: "100%" }}
          center={[lat, lng]}
          zoom={12}
          
        >
          <TileLayer
            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
            attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          />
          <UpdateCenter center={[lat, lng]} />
        </MapContainer>
    </div>

  );
};
OneQ
  • 1,149
  • 1
  • 8
  • 15
  • OK, that looks better and I do get latitude and longitude on other component through props when submit new ip address but why doesn't CreateMap component re-render? Correct me if I am wrong but when state is changed component should re-render, right? – dean89 May 13 '23 at 17:09
  • Check the edit. It does render but it's the MapContainer that doesn't want to update the center. You need to add another component – OneQ May 13 '23 at 17:32
  • why do we need another component to update the center? is this specifically for react-leaflet? or because I used react-query to fetch the api? thank you for your help and answers! – dean89 May 13 '23 at 18:54
  • 1
    It is due to react-leaflet – OneQ May 13 '23 at 18:54