How to add key property to list mapping

My app displays list of names of users, when a user clicks on a name it should open a new page with users full details. But when the link is clicked it opens the page without
displaying users details.

I know the challenge is adding key property to the Link from react-router-dom but i can
seem to implement it. See the code below

import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";

const User = () => {
  const [error, setError] = useState(null);
  const [isLoaded, setIsLoaded] = useState(false);
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/users/")
      .then((res) => res.json())
      .then(
        (data) => {
          setIsLoaded(true);
          setUsers(data);
        },
        (error) => {
          setIsLoaded(true);
          setError(error);
        }
      );
  }, []);
  if (error) {
    return <div>Error: {error.message}</div>;
  } else if (!isLoaded) {
    return <div>Loading...</div>;
  } else {
    return (
      <ul>
        {users.map((user) => (
          <li key={user.id}>
            <Link to={`user/${user.id}`}>{user.name}</Link>
          </li>
        ))}
      </ul>
    );
  }
};
import React, { useState, useEffect } from "react";

const UserDetail = (props) => {
  var id = props.match.params.id;
  const [error, setError] = useState(null);
  const [isLoaded, setIsLoaded] = useState(false);
  const [user, setUser] = useState([]);
  const [userAddress, setUserAddress] = useState([]);
  const [userCompany, setUserCompany] = useState([]);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/users/" + id)
      .then((res) => res.json())
      .then(
        (data) => {
          console.log(data);
          setIsLoaded(true);
          setUser(data);
          setUserAddress(data.address);
          setUserCompany(data.company);
        },
        (error) => {
          setIsLoaded(true);
          setError(error);
        }
      );
  }, []);
  if (error) {
    return <div>Error: {error.message}</div>;
  }
  if (!isLoaded) {
    return <div>Loading...</div>;
  }

  if (user) {
    return (
      <div>
        <h1>{user.name}</h1>
        <div>Email: {user.email}</div>
        <div>Phone: {user.phone}</div>
        <div>Website: {user.website}</div>
        <div>Company: {userCompany.name}</div>
        <div>
          Address: {userAddress.street}, {userAddress.suite}, {userAddress.city}
          , {userAddress.zipcode}
        </div>
      </div>
    );
  }
};
export default UserDetail;

Kindly look through the code and profer a solution

Thank you

I’m not sure that it has anything to do with a key. But as with your other thread it is a bit hard to be sure without seeing more of your code.

My guess would be that it is something to do with React Router, which introduced a number of breaking changes in its latest version.

Assuming the latest version of React Router, you can do it like this:

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import Users from './Users';
import User from './User';
import {
  BrowserRouter,
  Routes,
  Route,
} from "react-router-dom";

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <Routes>
      <Route path="users" element={<Users />} />
      <Route path="users/:id" element={<User />} />
    </Routes>
  </BrowserRouter>
);

Users.js

import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";

const Users = () => {
  const [error, setError] = useState(null);
  const [isLoaded, setIsLoaded] = useState(false);
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/users/")
      .then((res) => res.json())
      .then(
        (data) => {
          setIsLoaded(true);
          setUsers(data);
        },
        (error) => {
          setIsLoaded(true);
          setError(error);
        }
      );
  }, []);
  if (error) {
    return <div>Error: {error.message}</div>;
  } else if (!isLoaded) {
    return <div>Loading...</div>;
  } else {
    return (
      <ul>
        {users.map((user) => (
          <li key={user.id}>
            <Link to={`${user.id}`}>{user.name}</Link>
          </li>
        ))}
      </ul>
    );
  }
};

export default Users;

User.js

import React, { useState, useEffect } from "react";
import { useParams } from "react-router-dom";

const User = (props) => {
  const { id } = useParams();
  const [error, setError] = useState(null);
  const [isLoaded, setIsLoaded] = useState(false);
  const [user, setUser] = useState([]);
  const [userAddress, setUserAddress] = useState([]);
  const [userCompany, setUserCompany] = useState([]);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/users/" + id)
      .then((res) => res.json())
      .then(
        (data) => {
          console.log(data);
          setIsLoaded(true);
          setUser(data);
          setUserAddress(data.address);
          setUserCompany(data.company);
        },
        (error) => {
          setIsLoaded(true);
          setError(error);
        }
      );
  }, [id]);
  if (error) {
    return <div>Error: {error.message}</div>;
  }
  if (!isLoaded) {
    return <div>Loading...</div>;
  }

  if (user) {
    return (
      <div>
        <h1>{user.name}</h1>
        <div>Email: {user.email}</div>
        <div>Phone: {user.phone}</div>
        <div>Website: {user.website}</div>
        <div>Company: {userCompany.name}</div>
        <div>
          Address: {userAddress.street}, {userAddress.suite}, {userAddress.city}
          , {userAddress.zipcode}
        </div>
      </div>
    );
  }
};
export default User;

The main difference to your version is how to get the user id from the URL using the useParams hook.


As an aside, there is no need to make two fetch requests. All of the info that you are requesting in the second request is present in the response from the first.

If I was you, I would make use of the <Link> component’s state property to pass what you require:

Users.js

<ul>
  {users.map((user) => (
    <li key={user.id}>
      <Link to={`${user.id}`} state={{ user: users[user.id] }}>
        {user.name}
      </Link>
    </li>
  ))}
</ul>

User.js

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

const User = (props) => {

  const location = useLocation();
  const currUser = location.state.user;
  console.log(currUser)
  
  ...
}

And you eliminate the need for that second Ajax request.

LMK, if that is what you are after and I can stick you a small demo up on GitHub.

FWIW, here is the revised User component:

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

const User = () => {
  const location = useLocation();
  const user = location.state.user;
  return (
    <>
      <h1>{user.name}</h1>
      <div>Email: {user.email}</div>
      <div>Phone: {user.phone}</div>
      <div>Website: {user.website}</div>
      <div>Company: {user.company.name}</div>
      <div>
        Address: {user.address.street},{' '}
                 {user.address.suite},{' '}
                 {user.address.city},{' '}
                 {user.address.zipcode}
      </div>
    </>
  );
};
export default User;

Thank you