Home > OS >  React Cannot convert undefined or null to object
React Cannot convert undefined or null to object

Time:08-19

im 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

CodePudding user response:

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>;
}

CodePudding user response:

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>}
)

CodePudding user response:

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>)
     </>
)
  • Related