Search code examples
reactjscomponentsreact-leaflet

ReactJS ans react-Leaflet: how to make a component wait until the data of an API if load


I use React-leaflet to produce maps on my ReactJS app. The maps works perfectly when I use a .json file with the data in a GeoJSON format.

Now I have to use a data from our own API. I call the API and the datas are properly return by it. But there is my problem :

When I don't put the data in my component, the console.log of the state return {} (the initaialization) and then it reload automatically and the return the datas I need. But when I put the data in my component I've got an error and the state doesn't update at all.

The code with the API :

import React, {useEffect} from 'react';
import {useLocation} from 'react-router-dom'
import Graph3Vagues from './Graphiques/Troisvagues'
import CarteRisquesREGCour from './Cartes/Cartes_risques_rendement/cartes_risques_REG_cour'
import LegendeCarteRiskRDT from './Cartes/legendes_risques_rendement';

function RisquesRdt() {
  const { state } = useLocation();
  const { idExploit } = state;

  const [dataReg, setDataReg] = React.useState({})

  const [Risques7, setR7] = React.useState(0)
  const [Risques20, setR20] = React.useState(0)
  const [Risques50, setR50] = React.useState(0)

  const [dataPerso, setDP]= React.useState();
  const [dataVagues7, setDV7]= React.useState();
  const [dataVagues20, setDV20]= React.useState();
  const [dataVagues50, setDV50]= React.useState();

  const [maximum, setmax]= React.useState();
  const [dataQ1, setDQ1]= React.useState();
  const [dataQ2, setDQ2]= React.useState();
  const [dataQ3, setDQ3]= React.useState();
  const [Taille, setTaille]= React.useState();


  useEffect ( () => {
    fetch('API URL' + idExploit)
  .then( (response) => {
    return response.json()
  })
  .then(response =>{
    setR7(response.risques7)
    setR7(prev => {
    return prev;
    })
    setR20(response.risques20)
    setR20(prev => {
    return prev;
    })
    setR50(response.risques50)
    setR50(prev => {
    return prev;
    })
  }) 

    fetch('API URL' + idExploit)
    .then( (response) => {
        return response.json()
    })
    .then(response =>{
      setDP(prev => {
          return prev;
      })
      setDV7(response.dataVagues7)
      setDV7(prev => {
          return prev;
      })
      setDV20(response.dataVagues20)
      setDV20(prev => {
          return prev;
      })
      setDV50(response.dataVagues50)
      setDV50(prev => {
          setmax(prev[prev.length-1].y+20)
          setDQ1([{x:prev.length/4, y:0}, {x:prev.length/4, y:80}])
          setDQ2([{x:prev.length/2, y:0}, {x:prev.length/2, y:80}])
          setDQ3([{x:prev.length*3/4, y:0}, {x:prev.length*3/4, y:80}])
          setTaille(prev.length)
          return prev;
      })
    }) 

    fetch('API URL' + idExploit)
    .then( (response) => {
        return response.json()
    })
    .then(response =>{
      let idPRA 
      let idReg
      idPRA = response.idPetiteRegionAgricole
      idReg = response.idRegion
      fetch('API URL' + idReg)
      .then( (response) => {
        return response.json()
      })
      .then(response =>{
        setDataReg(response.data)
        setDataReg(prev => {
          return prev;
        })
      }) 
    })
  }, [idExploit])

    

  return (
    <div >
      <Entete titre="Risques rendement"/>
      <div className='container'>
        <div className='titre'>Quel est mon niveau de risques ? </div>
        <div className='titre'>{console.log(dataReg)} </div>
        <div className='paragraphe'>Une comparaison aux {Taille} exploitations spécialisées en grandes cultures, représentatives des régions agricoles françaises. Résultats sont représentés en % de pertes par rapport au produit brut.</div>
        <div className='paragraphe'> Votre exploitation est représentée par un trait noir </div>

        <div className='dispLigne'>
          <div className='dispLeftOpt'>
            <div style={{marginTop:'10px'}}>
              <Graph3Vagues dataPerso={dataPerso} dataVagues7={dataVagues7} dataVagues20={dataVagues20} dataVagues50={dataVagues50} maximum={maximum} dataQ1={dataQ1} dataQ2={dataQ2} dataQ3={dataQ3} />
            </div>
            <div className='row axeX'>
              <div className='textX' >25% les moins exposées </div>
              <div className='textX'>50%</div>
              <div className='textX'>25% les plus exposées</div>
            </div>
          </div>
        </div>
          {(TypeRisque==='Courant' && Echelle==='REG') ? (<CarteRisquesREGCour data={dataReg}/>)}
      </div>
      <OADMenu idExploit={idExploit}/>
    </div>
  );
}

export default RisquesRdt;

The code of the map :

import React from 'react';
import { MapContainer, TileLayer, GeoJSON } from 'react-leaflet'
import 'leaflet/dist/leaflet.css';


function CarteRisquesREGCour({data}) {
  const bbox = require('geojson-bbox');
  const extent = bbox(data); 

  function getColor(d) {
      return    d > 3500 ? '#530d0d' :
              d > 2750 ? '#530d0d' :
              d > 2500 ? '#69120e' :
              d > 2000 ? '#7f1810' :
              d > 1750 ? '#941d11' :
              d > 1500 ? '#aa2213' :
              d > 1000 ? '#c02714' :
              d > 750 ? '#cb3915' :
              d > 670 ? '#cf5315' :
              d > 650 ? '#d36e15' :
              d > 600 ? '#d78815' :
              d > 520 ? '#dba215' :
              d > 490 ? '#debd15' :
              d > 465 ? '#e2d715' :
              d > 430 ? '#d6d616' :
              d > 400 ? '#c7ce17' :
              d > 360 ? '#b7c517' :
              d > 320 ? '#a8bd18' :
              d > 275 ? '#98b519' :
              d > 210 ? '#89ad1a' :
              d > 190 ? '#6f9917' :
              d > 165 ? '#558415' :
              d > 140 ? '#3b6f12' :
              d > 110 ? '#205a10' :
              d > 0 ? '#06450e' :
              '#ffffff'; //le dernier on ne met pas son nom car le reste
    }
    function style(feature) {
        return {
          fillColor: getColor(feature.properties.risques7),
          weight: 0.5,
          opacity: 1,
          color: '#000000',
          dashArray: '3',
          fillOpacity: 0.7,
        };
      }

    const [selected, setSelected] = React.useState({});

    function highlightFeature(e) {
      var layer = e.target;
      const { risques7, libelle} = e.target.feature.properties;
      setSelected({
          risques:risques7,
          libelle:libelle,
      });
      layer.setStyle({
      weight: 5,
      color: '#666',
      dashArray: '',
      fillOpacity: 0.7
      });
    }
    function resetHighlight(e) {
      setSelected({});
      e.target.setStyle(style(e.target.feature));
    }
    function onEachFeature(feature, layer) {
      layer.on({
        click: highlightFeature,
        mouseout: resetHighlight
      });
    }
    return (
      <div className="totalCarte">
          <div className="">
          {!selected.risques && (
          <div className='paragraphe infos_carte'>
              Cliquez sur une zone pour avoir plus de détails
          </div>
          )}
          {selected.risques && (
              <div className='paragraphe infos_carte' >
                  {selected.libelle} : {selected.risques} €/ha
              </div>
          )}
          <MapContainer
            zoom={10}
            scrollWheelZoom={false}
            maxZoom={14}
            center={[48.832,2.623]}
            whenReady={e => {
              e.target.flyToBounds([
                [extent[1],extent[0]],
                [extent[3], extent[2]]
                  
                ]);
            }}
          >
            <TileLayer
              attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
              url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
            />
            {data && (
              <GeoJSON data={data} 
              style={style} 
              onEachFeature={onEachFeature} />
            )}
          </MapContainer>
        </div>
        
      </div>
    );
  }

export default CarteRisquesREGCour;

What I don't understand is that the chart "Graph3Vagues" is waiting to have the date from it's API but not the map.

I tried

  • create an async function with an await for the API but it doesn't do the trick
  • put an if dataReg!== {} show the component else not but I have still the error

Can someone help me please ?


Solution

  • Thanks to an adapatation of the validating answer of [this question][1], I solved my probleme, by adding a loading state.

    I add in the code with the API :

    function RisquesRdt() {
      const { state } = useLocation();
      const { idExploit } = state;
      const [loading, setLoading] = React.useState(true);
      ...
    
      useEffect ( () => {
       ...
          .then(response =>{
            setDataReg(response.data)
            setDataReg(prev => {
              setLoading(false);
              return prev;
            })
          }) 
        })
      }, [idExploit])
    ...
              {(TypeRisque==='Courant' && Echelle==='REG') ? (<CarteRisquesREGCour data={dataReg} loading={loading}/>)}
          </div>
          <OADMenu idExploit={idExploit}/>
        </div>
      );
    }
    
    export default RisquesRdt;
    

    And in the map file :

    import React from 'react';
    import { MapContainer, TileLayer, GeoJSON } from 'react-leaflet'
    import 'leaflet/dist/leaflet.css';
    
    
    function CarteRisquesREGCour({data, loading}) {
      const bbox = require('geojson-bbox');
      const extent = bbox(data); 
        ...
        return (
          <div className="totalCarte">
          {!loading && (
              <div className="">
              ...
                {data && !loading && (
                  <GeoJSON data={data} 
                  style={style} 
                  onEachFeature={onEachFeature} />
                )}
              </MapContainer>
            </div>
          )}
          </div>
        );
      }
    
    export default CarteRisquesREGCour;
    ```
    
    And with everything run works like a charm (except the highlight)
    
      [1]: https://stackoverflow.com/questions/73432932/api-is-not-working-inside-oneachfeature-react-leaflet-function