1

we got an issue, project on : Symfony 4, Api-Platform, React and other packages.

We create SearchBar.jsx and want to show the results on Maps.jsx

We want to transfert perfectAdress from the usestate of SearchBar.jsx to const position = [] on Maps.jsx

We got a good json response on our console log but we don't know how to retrieve the data and pass them.

We follow the trails of Context API, SWR and others, but we are stuck, so if you can help us we will be so happy.

Here's our code :

SearchBar.jsx :


import React, {useState} from "react";
import axios from 'axios';
import { withRouter } from 'react-router-dom';
import { toast } from "react-toastify";

const SearchBar = ({ history }) => {

    const [search, setSearch] = useState("");

    const [address, setAddress] = useState([]);

    const [perfectAddress, setPerfectAddress] = useState([]);

    const handleSearch = ({ currentTarget }) => {
        setSearch(currentTarget.value);
    }

    const handleSubmit = async event => {

        event.preventDefault();

            try {
               const data = await axios.get("https://nominatim.openstreetmap.org/search?q="+search+"&format=json&polygon=1&addressdetails=1")
                                       .then(reponse => reponse.data[0])

            setAddress(data);

            const latLong = [data['lat'], data['lon']];

            setPerfectAddress(latLong);

            history.push("/leaflet")
            } 
            catch (error) {
              toast.error("Impossible de charger les données");
            }
        }
        //history.replace("/leaflet")


    return (
    <>
        <form onSubmit={handleSubmit}>
            <div className="md-form active-cyan active-cyan-2 d-flex mb-3">
                <input 
                    className="form-control mr-3" 
                    type="text"
                    value={search}
                    onChange={handleSearch}
                    placeholder="Rechercher..." 
                    aria-label="Search" 
                />
                <button type="submit" className="btn btn-success">
                    Rechercher
                </button>          
            </div>
        </form>
    </>
  );
};

export default withRouter(SearchBar);

Json Reponse from SearchBar.jsx : Json Response

Maps.jsx :

import React, { useState, useEffect } from "react";
import axios from "axios";
import { Map, Marker, Popup, TileLayer } from "react-leaflet";
import { toast } from "react-toastify";

const Maps = (props) => {
  const [coordinates, setCoordinates] = useState([]);

  const fetchCoords = async () => {
    try {
      const data = await axios
        .get("http://localhost:8000/api/locations")
        .then((reponse) => reponse.data["hydra:member"]);

      setCoordinates(data);
    } catch (error) {
      toast.error("Impossible de charger les données");
    }
  };

  useEffect(() => {
    fetchCoords();
  }, []);

  const position = [49.442402, 1.09846];

  return (
    <>
      <Map center={position} id="mapid" zoom={12}>
        <TileLayer
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          maxZoom="18"
          id="mapbox/streets-v11"
        />
        {coordinates.map((maps) => {
          const ensemble = [maps.latitude, maps.longitude];
          const adresse = maps.address;
          return (
            <Marker position={ensemble}>
              <Popup>{adresse}</Popup>
            </Marker>
          );
        })}
      </Map>
    </>
  );
};

export default Maps;

nabuko81
  • 17
  • 5

4 Answers4

1

This is probably the perfect scenario for using context under the assumption that the two components are rendered deep into separate branches.

So if you look at the following code:

const MyContext = React.createContext({ position: [], setPosition: () => {} })

function MyParentComponent() {
  const [position, setPosition] = useState([])

  return (
    <MyContext.Provider value={{ position, setPosition }}>
      <SearchBar />
      <Maps />
    </MyContext.Provider>
  )
}

We declare a context with a default value (this is the value provided if you consumer the context outside of this component tree). Inside of the parent component, we declare some state and the key is that we now provide this state to all components beneath this one via value={{ position, setPosition }}

In the maps and search bar component, you can now write const { position, setPosition } = useContext(MyContext)

And then you can replace setPerfectAddress(latLong); with setPosition(latLong); then in the map you can just use position now that is stateful.

Tom Finney
  • 2,670
  • 18
  • 12
  • We tried your solution and it actually works very well. However in your example, the searchbar component is on the same page as the maps one. That's not our case and we don't know how to put the result of the searchbar component into the maps component, which is on a different page. Arrrrgggg. – nabuko81 Apr 14 '20 at 11:10
  • Its alive, its alive, :D we adapt your proposition, and thats working. Tk – nabuko81 Apr 17 '20 at 06:43
0

Actually, if you want to save value between routers you can use


1) useContext provide from React team

2) custom state mobx-state-tree or redux

3) save perfectAdress in local storage (not recommended)

4) pass as second parameter in location.push(url, yourArgs)

Egor Pashko
  • 354
  • 2
  • 8
0

You can pass a second argument into history.push(), which can be anything you want to pass along, which then can be accessed from the location's props:

history.push({  
    pathname: '/page',  
    state: { detail: 'some_value' }  
});

And you can access it like this:

import { useEffect } from "react";
import { useLocation } from "react-router-dom";

const Page= props => {
    const location = useLocation();

    useEffect(() => {
       console.log(location.pathname); // result: '/page'
       console.log(location.state.detail); // result: 'some_value'
    }, [location]);

};

See more at: How to pass params with history.push/Link/Redirect in react-router v4?

maten
  • 566
  • 6
  • 17
0

You can pass your data as props to navigated component e.g.

change this

history.push("/leaflet")

to

history.push({
  pathname: '/leaflet',
  latlng
});

where latlng being the data to be sent

Now in Maps component, you can get this data from props

const { latlng } = props.location;
Zohaib Ijaz
  • 21,926
  • 7
  • 38
  • 60