1

I am trying to render leaflet markers using React Leaflet with data fetched from an API using useEffect. The markers are not showing up despite the location data being non-null at the time of declaring the marker (supposedly, through the use of conditional rendering, though I'm not so sure anymore).

The code for the useEffect:

const [arr, setArr] = React.useState([]);
const [dataLoaded, setDataLoaded] = React.useState(false);

useEffect(() => {
    axios.get('http://localhost:8000/get-data/')
    .then(res => {
        if (res.data) {
          const data = res.data
          for (let i = 0; i < data.length; i++) {
            arr.push(data[i])
          }
        }
    })
    .then(res => {
      if ( arr.length > 0) {
        setDataLoaded(true)
      }
    })
}, [])

The code for rendering the markers:

{ dataLoaded ? console.log(arr[0].latitude, arr[0].longitude) : null}
{ dataLoaded ? <Marker position={[parseFloat(arr[0].latitude), parseFloat(arr[0].longitude)]}> 
    <Popup>
        A pretty CSS3 popup. <br /> Easily customizable.
    </Popup>
</Marker> : <></> }

This is an example of me trying to render the first set of coordinates from my data array. I included the console logs for debugging, which output the values 36.77 & -199.41 for the latitude and longitude respectively. These are the same variables being used for the marker's position. For some reason, though, the marker does not show up on my map. If I enter the values [36.77, -119.41] directly into its position, then the marker does show up. Any ideas on why this is happening?

Edit: here is a screenshot of my data array after being filled.

2 Answers 2

2

I would change your code to something like this

const [arr, setArr] = React.useState([]);
const [dataLoaded, setDataLoaded] = React.useState(false);
useEffect(() => {
    axios.get('http://localhost:8000/get-data/')
    .then(res => {
        if (res.data) {
          const data = res.data
          const items = []
          data.forEach(item => items.push(item))
          setArr(items)
        }
    })
    .then(res => {
      //here remove the condition arr.length > 0
      //instead you should have conditional rendering in your app
      //so if the data is loaded display <>No Items Found</> or something
      setDataLoaded(true)
    })
}, [])

the reason you want to remove the conditional setDataLoaded(true) is because what if you fetch and the fetch returns [], then your dataLoaded is false even though it actually is done loading. Also are you sure that you are fetching the data? like if you add console.log(data) in your fetch you see it loading?

There is also a hackish way of forcing a rerender by doing something like this

const [isRerender, setIsRerender] = React.useState(false)

then you would want to add something like this at the end of your api call

.finally(() => {setIsRerender(!isRerender)})

I do NOT recommend this but I have used it before and it is a possibility. But this should really be last resort type of thing because your code should be working.

If any of my code is confusing or does not make sense, let me know and I'll explain in more depth.

1
  • 1
    Hi, thank you for commenting! I resolved the issue and it was unrelated to the useEffect, however, I agree that this method should be preferred.
    – fault
    Commented May 15 at 21:44
1

First, updating the state like arr should be within setArr, also you need to change the array reference, using push to update arr will not make the component re-rendered.

First a useEffect with empty dependence, you set the arr value.

useEffect(() => {
    axios.get('http://localhost:8000/get-data/')
    .then(res => {
        if (res.data) {
            setArr(prevArr => [...prevArr, ...res.data]);
        }
    })
}, []);

Then other useEffect with arr dependency to set the dataLoaded

useEffect(() => {
    if (arr.length > 0) {
        setDataLoaded(true);
    }
}, [arr]);
0

Not the answer you're looking for? Browse other questions tagged or ask your own question.