Home > Enterprise >  Link from user cards to user page with user full info and zoomed map location with create react app
Link from user cards to user page with user full info and zoomed map location with create react app

Time:09-01

I am creating a simple app that displays a list of user cards from a JSON file and their location on a leaflet map.

That part has been done but now I need to implement functionality to link to each user's page with more info about the user as well as their zoomed in location on the map.

This is my App.js file:

import React from "react";
import { BrowserRouter as Router } from "react-router-dom";
import { Routes, Route } from "react-router-dom";
import UserList from "./pages/UserList";
import UserProfile from "./pages/UserProfile";
import "./App.css";


function App() {

  return (
  <Router>
        <Routes>
        <Route path="/" element={<UserList/>} />
          <Route path="/pages/UserList" element={<UserList/>} />
          <Route path="/pages/UserProfile" element={<UserProfile/>}/>
        </Routes>
    </Router>
  );
}

export default App;

This is my UserList page where I fetch the data and list it in user cards as well as in the leaflet map with markers related to each user location:

import { useState, useEffect } from "react";

import UserProvider, { useAppContext } from "../components/UserProvider";
import UserCard from "../components/UserCard";
import Markers from "../components/Markers";

import { MapContainer, TileLayer, useMap } from "react-leaflet";
import "../App.css";


function SetMapView() {
  const { user } = useAppContext();
  const map = useMap();

  useEffect(() => {
    if (!user) return;

    const { lat, lng } = user.address.geo;

    map.flyTo([lat, lng], 5);
  }, [user]);

  return null;
}

function UserList() {
  const [users, setUsers] = useState([]);
  const [data, setData] = useState([]);

  useEffect(() => {
    (async () => {
      let userData;
      try {
        const response = await fetch(
          "https://jsonplaceholder.typicode.com/users/"
        );
        userData = await response.json();
      } catch (error) {
        console.log(error);
        userData = [];
      }

      setUsers(userData);
      setData(userData);

      // Map
    })();
  }, []);

  return (
    <UserProvider>
      <div className="App">
        <div className="cards-container">
          {users.map((user, index) => (
            <UserCard userData={user} key={index} />
          ))}
        </div>

        <div className="map-container">
          <MapContainer
            center={[47.217324, 13.097555]}
            zoom={0}
            style={{ height: "100vh" }}
          >
            <TileLayer
              url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
              attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
            />

            <Markers data={data} />
            <SetMapView />
          </MapContainer>
        </div>
      </div>
    </UserProvider>
  );
}

export default UserList;

This is my UserCar component:

import './UserCard.css';
import { Link } from "react-router-dom";


const UserCard = ({ userData }) => {
    return (

        <Link to="./pages/UserProfile">
        <div className="card">
            <div className="card-name">{userData.name}</div>

            <div className="card-body">
                <div className="card-email"><i className="fa fa-envelope" /> {userData.email}</div>
                <div className="card-phone"><i className="fa fa-phone" /> {userData.phone}</div>
                <div className="card-website"><i className="fa fa-globe" /> {userData.website}</div>
                <div className="card-company"><i className="fa fa-briefcase" /> {userData.company.name}</div>
            </div>
        </div>
        </Link>
    );
};

export default UserCard;

And then when clicking on these user cards they would lead to individual user profile pages containing full user information and their zoomed in location on the leaflet map.

I already have a user details component to display the full list of user info and this is the code:


const UserDetails = ({ userData }) => {

    
    return (
       
        <div className="card">
            <div className="card-name">{userData.name}</div>

            <div className="card-body">
                <div className="card-username"><i className="fa fa-envelope" /> {userData.username}</div>
                <div className="card-email"><i className="fa fa-envelope" /> {userData.email}</div>
                <div className="card-address"><i className="fa fa-envelope" /> {userData.address.street}, {userData.address.suite}, {userData.address.city}, {userData.address.zipcode}, {userData.address.geo.lat}, {userData.address.geo.lng}</div>
                <div className="card-phone"><i className="fa fa-phone" /> {userData.phone}</div>
                <div className="card-website"><i className="fa fa-globe" /> {userData.website}</div>
                <div className="card-company"><i className="fa fa-briefcase" /> {userData.company.name}, {userData.company.catchPhrase}, {userData.company.bs}</div>
            </div>
        </div>

    );
};

export default UserDetails;

And then this is the page for displaying the full user data:

import UserDetails from "../components/UserDetails";


function UserProfile({ userData }) {

  
    return (

    <UserDetails userData={userData} />

    );
  }
  export default UserProfile;

And that's where I'm stuck. I am not sure how to pull and sort and display specific user data here so this page can display full user data and map with user zoomed in location.

Any help would be appreciated.

CodePudding user response:

You have to follow a different approach to achieve this. UserProvider will no longer help you. You need to do several changes:

  1. UserModal is gone. You do not need it.

  2. You need to pass the user id when clicking on the User details to fetch each user specific data when navigating to UserProfile route. Using useParams you get the id, then fetch the specific user data and then building the card with that data. Now for the map you need to make the map a separate comp where you will have two use cases. One render all the markers with all users. Second you render only one marker with single user data.

      function UserProfile() {
       const { id } = useParams();
    
       const { user } = useGetUser(id);
    
       const navigate = useNavigate();
    
       const handleClick = () => {
         navigate("/");
       };
    
       return user && id ? (
         <>
           <UserDetails userData={user} />
           <Button onClick={handleClick}>Go back</Button>
           <Map user={user} />
         </>
       ) : null;
     }
    
    
    function Map({ users, user }) {
       return (
         <div className="map-container">
           <MapContainer
             center={[47.217324, 13.097555]}
             zoom={0}
             style={{ height: "100vh" }}
           >
             <TileLayer
               url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
               attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
             />
             <Markers users={users} user={user} />
             {user && <SetMapView user={user} />}
           </MapContainer>
         </div>
       );
     }
    

I customized UserMarker by creating one case to loop over all markers and another to use a single marker. There is many more. You can check the demo

  • Related