Aasem Shoshari
Aasem Shoshari

Reputation: 17

React Cannot convert undefined or null to object

I'm fetching employees collection into employees state:

...
  const [employees, setEmployees] = useState([]);
  useEffect(() => {
    const getEmployees = async () => {
      const response = await fetch("http://localhost:8000/get-employees");
      const data = await response.json();
      setEmployees(data);
    };
    getEmployees();
  }, []);

...

The state will look like this:

employees:[
{ _id:13413413413,nameAR:"عاصم شوشاري",nameEN:"aasem shoshari",title:"front-end software engineer" }
{ _id:13413413413,nameAR:"عاصم شوشاري",nameEN:"aasem shoshari",title:"front-end software engineer" }
{ _id:13413413413,nameAR:"عاصم شوشاري",nameEN:"aasem shoshari",title:"front-end software engineer" }
]

Then displaying the data into html table:

return:(
        <table>
          <tr key={"header"}>
            { Object.keys(employees[0]).map((key) => <th>{key}</th>)}
          </tr>
          { employees.map((employee) => (
              <tr key={employee._id}>
                {Object.values(employee).map((val) => (
                  <td>{val}</td>
                ))}
              </tr>
            ))}
        </table>
)

it looks like the state value will take some time to be fetched, therefore the Object.keys(employees[0]) will be null for some time and the application will crash and it says Cannot convert undefined or null to object

although im using useEffect isn't suppose that the component will re-render on state change, then will it crash anyway therefore you need a way to tell the component to wait for the state? but how

Upvotes: 1

Views: 7520

Answers (3)

Shubham Jasani
Shubham Jasani

Reputation: 165

The best practice is to always set loader if you call API

const [employees, setEmployees] = useState([{}]);
const [loader,setLoader] = useState(false)
  useEffect(() => {
    const getEmployees = async () => {
      setLoader(true);
      const response = await fetch("http://localhost:8000/get-employees");
      const data = await response.json();
      setLoader(false);
      setEmployees(data);
    };
    getEmployees();
  }, []);

after setLoader

return(
     <>
        { loader? <div>Loading....</div> :(<table>
          <tr key={"header"}>
            { Object.keys(employees[0]).map((key) => <th>{key}</th>)}
          </tr>
          { employees.map((employee) => (
              <tr key={employee._id}>
                {Object.values(employee).map((val) => (
                  <td>{val}</td>
                ))}
              </tr>
            ))}
        </table>)
     </>
)

Upvotes: 0

Matt Pengelly
Matt Pengelly

Reputation: 1596

dont display the rows in the table until employees is populated.

return( 
       {employees.length > 0 ??
        <table>
          <tr key={"header"}>
            { Object.keys(employees[0]).map((key) => <th>{key}</th>)}
          </tr>
          {employees.map((employee) => (
              <tr key={employee._id}>
                {Object.values(employee).map((val) => (
                  <td>{val}</td>
                ))}
              </tr>
            ))}
        </table> : <p>not ready</p>}
)

Upvotes: 0

jedrzej.kurylo
jedrzej.kurylo

Reputation: 40899

Yes, once data is fetched and employees state value set, the component will re-render. But until then, you need to handle the initial state, when employees is undefined.

The simplest way to do that would be:

if (employees) {
  return <table>...</table>;
} else {
  return <div>Loading...</div>;
}

Upvotes: 1

Related Questions