skip to Main Content

I am fetching a list of items from a local Data.js file and displaying it using map method in Header.js. But when I click on user image then it should go to user detail page and should display all the properties of that object. But when I click it gives me error saying "Cannot read properties of undefined (reading 'name')". I am using react-router-domv6.

Users.js

import React from 'react';
import { Link } from 'react-router-dom';
import { list } from './Data';

function Header() {
  console.log(list);
  return (
    <div>
      <div className="bg-gradient-to-b from-white-300 to-[#6337c8] relative flex justify-center items-center h-screen w-screen">
        <div className="absolute top-0 w-full z-[-1]">
          <div className="h-[20vh] bg-[#6337c8]"></div>
          <svg className="drop-shadow-[0px_17px_0px_#dcd6f3] z-[-1]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320">
            <path
              fill="#6337c8"
              fillOpacity={1}
              d="M 0 224 L 80 197.3 C 160 171 320 117 480 128 C 640 139 800 213 960 245.3 C 1120 277 1280 267 1360 261.3 L 1440 256 L 1440 0 L 1360 0 C 1280 0 1120 0 960 0 C 800 0 640 0 480 0 C 320 0 160 0 80 0 L 0 0 Z"
            ></path>
          </svg>
        </div>
        <div className="h-[85vh] shadow-2xl w-2/5 rounded-[25px] bg-white">
          <div className="w-full rounded-t-[25px] flex justify-center items-center h-[16vh] text-gray-600 bg-gray-100 text-xl">
            {' '}
            Select an account{' '}
          </div>
          <div className="h-[65vh] pb-4 px7 overflow-y-auto scrollbar scrollbar-thumb-gray-200">
            {list &&
              list.length > 0 &&
              list.map((value) => (
                <Link key={value.id} to={`/profile/${value.id}`}>
                  <div className="cursor-pointer flex gap-3 items-center border-b py-2">
                    <img className="rounded-full w-[7%]" src={value.profilepicture} alt="" />
                    <p>City: {value.address.city}</p>
                    <p>Company Name: {value.company.name}</p>
                    <div className="text-md text-gray-700 text-center font-[400] ">
                      <button>{value.name}</button>
                    </div>
                  </div>
                </Link>
              ))}
          </div>
        </div>
      </div>
    </div>
  );
}

export default Header;

UserDetail.js

import React from 'react';
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useParams } from 'react-router-dom';
import { list } from './Data';

function Profile() {
  const [item, setItem] = useState({});
  const [show, setShow] = useState(false);

  const { id } = useParams();

  const navigate = useNavigate();

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

  const getItem = () => {
    const newItem = list.find((value) => value.id === parseInt(id));
    setItem(newItem);
  };

  const Logout = () => {
    navigate('/');
  };

  return (
    <div>
      <div className="w-screen h-screen flex gap-10 px-8 pt-3 overflow-x-hidden">
        <div className="flex flex-col w-full">
          <div className="h-[13vh] w-[100%] border-b border-gray-300">
            <div className="flex h-full items-center justify-between">
              <div className="capitalize text-[20px] text-gray-500">
                <p>profile</p>
              </div>

              <div className="relative inline-block text-left mt-5 ml-40" data-headlessui-state>
                {
                  <div>
                    <button
                      className="inline-flex w-full justify-center rounded-md bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm focus:outline-none "
                      id="headlessui-menu-button-:r0:"
                      type="button"
                      aria-haspopup="menu"
                      aria-expanded="false"
                      data-headlessui-state="open"
                      aria-controls="headlessui-menu-items-:r1:"
                      onClick={() => setShow(!show)}
                    >
                      <div className="cursor-pointer flex justify-end gap-3 items-center">
                        <img className="rounded-full w-[7%]" src={item.profilepicture} alt={item.id} />
                        <p>{item.name}</p>
                      </div>
                    </button>
                  </div>
                }

                {show && (
                  <div
                    className="absolute right-0 z-10 mt-2 w-[20vw] origin-top-right rounded-md bg-white shadow-lg focus:outline-none transform opacity-100 scale-100"
                    aria-labelledby="headlessui-menu-button-:r0:"
                    id="headlessui-menu-items-:r7:"
                    role="menu"
                    tabIndex={0}
                    data-headlessui-state="open"
                  >
                    <div className=" py-1" role="none">
                      <div className="flex flex-col justify-center items-center gap-1 pb-2 " role="none">
                        <img className="rounded-full" src={item.profilepicture} alt="" width="50%" role="none"></img>
                        <p role="none">{item.name}</p>
                        <p className="text-[14px] text-gray-400" role="none">
                          {item.email}
                        </p>
                        <div className="border-t border-gray-300 ">
                          <button className="mt-2 rounded-[20px] px-2 py-1 text-white bg-[#e15b22]" role="none" onClick={Logout}>
                            Sign out
                          </button>
                        </div>
                      </div>
                    </div>
                  </div>
                )}
              </div>
            </div>
          </div>

          <div className="block">
            <div className="w-full h-full flex mt-5">
              <div className="border-r border-gray-300 h-full w-[40%]">
                {
                  <div className="flex flex-col w-full items-center h-full justify-center">
                    <img className="rounded-full w-[48%]" src={item.profilepicture} alt="" />
                    <p className="text-[16px] font-[400] text-gray-800">{item.name}</p>

                    <div className="flex flex-col gap-2 border-b border-gray-300 py-3">
                      <div className="flex gap-3 items-center justify-center">
                        <p className="text-[16px] font-[400] text-gray-400">Username:</p>
                        <p className="text-[16px] font-[400] text-gray-800">{item.username}</p>
                      </div>

                      <div className="flex gap-3 items-center justify-center">
                        <p className="text-[16px] font-[400] text-gray-400">email:</p>
                        <p className="text-[16px] font-[400] text-gray-800">{item.email}</p>
                      </div>

                      <div className="flex gap-3 items-center justify-center">
                        <p className="text-[16px] font-[400] text-gray-400">phoneNumber:</p>
                        <p className="text-[16px] font-[400] text-gray-800">{item.phone}</p>
                      </div>
                      <div className="flex gap-3 items-center justify-center">
                        <p className="text-[16px] font-[400] text-gray-400">website:</p>
                        <p className="text-[16px] font-[400] text-gray-800">{item.website}</p>
                      </div>
                    </div>

                    <div className="flex flex-col gap-2 py-3">
                      <div className="flex gap-3 items-center justify-center">
                        <p className="text-[16px] font-[400] text-gray-400">company:</p>
                        <p className="text-[16px] font-[400] text-gray-800">{item.company.name}</p>
                      </div>

                      <div className="flex gap-3 items-center justify-center">
                        <p className="text-[16px] font-[400] text-gray-400">bs:</p>
                        <p className="text-[16px] font-[400] text-gray-800">{item.company.bs}</p>
                      </div>
                    </div>
                  </div>
                }
              </div>

              <div className="h-full w-[60%]">
                <div className="flex gap-3 items-center justify-center">
                  <p className="text-[16px] font-[400] text-gray-400">address:</p>

                  <p className="text-[16px] font-[400] text-gray-800">{item.address.city}</p>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

export default Profile;

Data.js

export const list = [
  {
    id: 1,
    name: ' Leanne Graham',
    username: 'Bret',
    email: '[email protected]',
    profilepicture: 'https://panorbit.in/wp-content/uploads/2019/hotlink-ok/1001.jpeg',
    address: {
      street: 'Kulas Light',
      suite: 'Apt. 556',
      city: 'Gwenborough',
      zipcode: '92998-3874',
      geo: {
        lat: '-37.3159',
        lng: '81.1496',
      },
    },
    phone: '1-770-736-8031 x56442',
    website: 'hildegard.org',
    company: {
      name: 'Romaguera-Crona',
      catchPhrase: 'Multi-layered client-server neural-net',
      bs: 'harness real-time e-markets',
    },
  },
  {
    id: 2,
    name: ' Ervin Howell',
    username: 'Antonette',
    email: '[email protected]',
    profilepicture: 'https://panorbit.in/wp-content/uploads/2019/hotlink-ok/1002.jpeg',
    address: {
      street: 'Victor Plains',
      suite: 'Suite 879',
      city: 'Wisokyburgh',
      zipcode: '90566-7771',
      geo: {
        lat: '-43.9509',
        lng: '-34.4618',
      },
    },
    phone: '010-692-6593 x09hotlink-ok5',
    website: 'anastasia.net',
    company: {
      name: 'Deckow-Crist',
      catchPhrase: 'Proactive didactic contingency',
      bs: 'synergize scalable supply-chains',
    },
  }
]

3

Answers


  1. You just need to change the initial state value to const [item, getItem] = useState(null); and then check if the item exists, render the data otherwise not.

    Please replace you UserDetail.js file that file which I updated in my answer.

    import React from 'react';
    import { useEffect, useState } from 'react';
    import { useNavigate } from 'react-router-dom';
    import { useParams } from 'react-router-dom';
    import { list } from './Data';
    
    function Profile() {
      const [item, setItem] = useState(null);
      const [show, setShow] = useState(false);
    
      const { id } = useParams();
    
      const navigate = useNavigate();
    
      useEffect(() => {
        getItem();
      }, []);
    
      const getItem = () => {
        const newItem = list.find((value) => value.id === parseInt(id));
        setItem(newItem);
      };
    
      const Logout = () => {
        navigate('/');
      };
    
      return (
        <div>
          <div className="w-screen h-screen flex gap-10 px-8 pt-3 overflow-x-hidden">
            <div className="flex flex-col w-full">
              <div className="h-[13vh] w-[100%] border-b border-gray-300">
                <div className="flex h-full items-center justify-between">
                  <div className="capitalize text-[20px] text-gray-500">
                    <p>profile</p>
                  </div>
    
                  <div className="relative inline-block text-left mt-5 ml-40" data-headlessui-state>
                    {item &&
                      <div>
                        <button
                          className="inline-flex w-full justify-center rounded-md bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm focus:outline-none "
                          id="headlessui-menu-button-:r0:"
                          type="button"
                          aria-haspopup="menu"
                          aria-expanded="false"
                          data-headlessui-state="open"
                          aria-controls="headlessui-menu-items-:r1:"
                          onClick={() => setShow(!show)}
                        >
                          <div className="cursor-pointer flex justify-end gap-3 items-center">
                            <img className="rounded-full w-[7%]" src={item.profilepicture} alt={item.id} />
                            <p>{item.name}</p>
                          </div>
                        </button>
                      </div>
                    }
    
                    {show && (
                      <div
                        className="absolute right-0 z-10 mt-2 w-[20vw] origin-top-right rounded-md bg-white shadow-lg focus:outline-none transform opacity-100 scale-100"
                        aria-labelledby="headlessui-menu-button-:r0:"
                        id="headlessui-menu-items-:r7:"
                        role="menu"
                        tabIndex={0}
                        data-headlessui-state="open"
                      >
                        <div className=" py-1" role="none">
                          <div className="flex flex-col justify-center items-center gap-1 pb-2 " role="none">
                            <img className="rounded-full" src={item.profilepicture} alt="" width="50%" role="none"></img>
                            <p role="none">{item.name}</p>
                            <p className="text-[14px] text-gray-400" role="none">
                              {item.email}
                            </p>
                            <div className="border-t border-gray-300 ">
                              <button className="mt-2 rounded-[20px] px-2 py-1 text-white bg-[#e15b22]" role="none" onClick={Logout}>
                                Sign out
                              </button>
                            </div>
                          </div>
                        </div>
                      </div>
                    )}
                  </div>
                </div>
              </div>
    
              <div className="block">
                <div className="w-full h-full flex mt-5">
                  <div className="border-r border-gray-300 h-full w-[40%]">
                    {item &&
                      <div className="flex flex-col w-full items-center h-full justify-center">
                        <img className="rounded-full w-[48%]" src={item.profilepicture} alt="" />
                        <p className="text-[16px] font-[400] text-gray-800">{item.name}</p>
    
                        <div className="flex flex-col gap-2 border-b border-gray-300 py-3">
                          <div className="flex gap-3 items-center justify-center">
                            <p className="text-[16px] font-[400] text-gray-400">Username:</p>
                            <p className="text-[16px] font-[400] text-gray-800">{item.username}</p>
                          </div>
    
                          <div className="flex gap-3 items-center justify-center">
                            <p className="text-[16px] font-[400] text-gray-400">email:</p>
                            <p className="text-[16px] font-[400] text-gray-800">{item.email}</p>
                          </div>
    
                          <div className="flex gap-3 items-center justify-center">
                            <p className="text-[16px] font-[400] text-gray-400">phoneNumber:</p>
                            <p className="text-[16px] font-[400] text-gray-800">{item.phone}</p>
                          </div>
                          <div className="flex gap-3 items-center justify-center">
                            <p className="text-[16px] font-[400] text-gray-400">website:</p>
                            <p className="text-[16px] font-[400] text-gray-800">{item.website}</p>
                          </div>
                        </div>
    
                        <div className="flex flex-col gap-2 py-3">
                          <div className="flex gap-3 items-center justify-center">
                            <p className="text-[16px] font-[400] text-gray-400">company:</p>
                            <p className="text-[16px] font-[400] text-gray-800">{item.company.name}</p>
                          </div>
    
                          <div className="flex gap-3 items-center justify-center">
                            <p className="text-[16px] font-[400] text-gray-400">bs:</p>
                            <p className="text-[16px] font-[400] text-gray-800">{item.company.bs}</p>
                          </div>
                        </div>
                      </div>
                    }
                  </div>
    
                  <div className="h-full w-[60%]">
                    <div className="flex gap-3 items-center justify-center">
                      <p className="text-[16px] font-[400] text-gray-400">address:</p>
    
                      <p className="text-[16px] font-[400] text-gray-800">{item?.address?.city}</p>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      );
    }
    
    export default Profile;
    
    Login or Signup to reply.
  2. Your initial state is a empty object {} which means the property name is not defined yet.

    To resolve this you can use optional chaining.

    <p>{item?.name}</p>
    

    Same for all the other expression which use item.

    Now for the useEffect, this will currently only ran the very first render. But will not update once you revisit to check another user. To fix this you can pass the id as a dependency.

    useEffect(() => {
      getItem();
    }, [id]);
    
    Login or Signup to reply.
  3. The issue is that in the Profile component the initial item state is an empty object {} which is fine when only accessing root-level properties, i.e. item.name, item.company, etc, but the issue is that during the initial render cycle and any subsequent render cycle until the item state is populated all the root-level properties will be undefined, so an error is thrown when attempting to access a second-level property like item.company.name. item.company is undefined and attempting to access item.company.name will throw the exception.

    The item "state" is completely unnecessary as it’s what we would call derived state, in that it is derived from the current id route path parameter and the imported list array. Derived state shouldn’t be stored in local state unless absolutely necessary.

    To resolve the issue simply compute the item value from the list and id values. I recommend also converting the data’s id property to a String instead of parsing the route path parameter to an integer as it’s generally easier to compare number-like strings.

    Example:

    import React from 'react';
    import { useEffect, useState } from 'react';
    import { useNavigate, useParams } from 'react-router-dom';
    import { list } from './Data';
    
    function Profile() {
      const navigate = useNavigate();
      const { id } = useParams();
    
      const item = list.find((value) => String(value.id) === id);
    
      const [show, setShow] = useState(false);
    
      const Logout = () => {
        navigate('/');
      };
    
      if (!item) {
        // No matching item found, render nothing, error message, etc, it's up to you
        return <div>No matching item found.</div>;
    
      }
    
      return (
        <div>
          ....
        </div>
      );
    }
    

    If the "cost" of computing the item value is "expensive" you can memoize the value with the useMemo hook.

    const item = useMemo(() => {
      return list.find((value) => String(value.id) === id)
    }, [id]);
    

    In fact, just about any time you see you’ve implemented the pattern useState + useEffect you should really be using useMemo.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search