Martin Wcr
Martin Wcr

Reputation: 35

How to iterate through a list of objects from api and render it to jsx page React functional component

I'm trying to get a list of array items from an api response using useEffect, as a result I'm getting a list of objects. I want to convert that using Objects.key() method, but when I iterate through it, values won't be printed on view page. The response keeps on returning the call from api response.

API CALL

 export function getServicesById(facility_id) {     
  return axios
        .get(`${baseURL}patients/appointments/1`)
        .then((response) => {
            if(response.status===200){
                // console.log(response.data)
                return response
            }
        }).catch(error => {
            console.log('error', error)
            return error
        });
    }

useEffect to get response data

  useEffect(() => {
    getServicesById()
      .then((res) => {
        setResponseData(res.data);
      })
      .catch((error) => {
        console.log(error);
      });
    // console.log('data is', newResponseData);
  }, [responseData, setResponseData]);

Converting objects to array items

 const newResponseData = Object.keys(responseData).reduce((array, key) => {
    return [...array, { key: responseData[key] }];
  }, []);

Jsx page where I'm rendering the data

<TableBody>              
  {newResponseData &&
    newResponseData.map((serv, index) => (
    <TableRow key={index}>
       <TableCell align="left" data-label="Action">
           <Field
              name="status"
              component={renderSelectInput}
              variant="outlined"
              size="small"
              className="fileds"
              inputClass="sm-filed"
            >
            <MenuItem selectedValue="progress">
               In progress
             </MenuItem>
             <MenuItem value="closed">
              Closed
             </MenuItem>
            </Field>
            </TableCell>
            <TableCell data-label="Id">{serv.id}</TableCell>
            <TableCell data-label="Patient name">{serv.patient_name}</TableCell>
            <TableCell data-label="Test Name">{serv.test_name}</TableCell>
        </TableRow>
      ))}
   </TableBody>

Upvotes: 2

Views: 1303

Answers (2)

Drew Reese
Drew Reese

Reputation: 202628

Issue

You are using an useEffect dependency that the effect updates:

useEffect(() => {
  getServicesById()
    .then((res) => {
      setResponseData(res.data);
    })
    .catch((error) => {
      console.log(error);
    });
}, [responseData, setResponseData]);

responseData is updated by the effect, and when updated will trigger another rerender and the effect will run again. You likely only want to fetch the data once when the component mounts.

Another potential issue I see is that the newResponseData array has objects with only a key key.

const newResponseData = Object.keys(responseData).reduce((array, key) => {
  return [...array, { key: responseData[key] }];
}, []);

But in the render you are attempting to access id, patient_name, and test_name properties. These are undefined since { key: responseData[key] } hasn't any of these properties.

Solution

Remove the useEffect dependencies so the effect runs once on component mount. Since I also think you meant to compute an array of the object values from responseData to map in your UI, you can do this once before sending the data to state.

useEffect(() => {
  getServicesById()
    .then((res) => {
      const data = Object.values(res.data)
      setResponseData(data);
    })
    .catch((error) => {
      console.log(error);
    });
}, []);

Then just map your responseData state:

<TableBody>              
  {responseData && responseData.map((serv, index) => (
    <TableRow key={index}>
       <TableCell align="left" data-label="Action">
           <Field
              name="status"
              component={renderSelectInput}
              variant="outlined"
              size="small"
              className="fileds"
              inputClass="sm-filed"
            >
            <MenuItem selectedValue="progress">
               In progress
             </MenuItem>
             <MenuItem value="closed">
              Closed
             </MenuItem>
            </Field>
            </TableCell>
            <TableCell data-label="Id">{serv.id}</TableCell>
            <TableCell data-label="Patient name">{serv.patient_name}</TableCell>
            <TableCell data-label="Test Name">{serv.test_name}</TableCell>
        </TableRow>
      ))}
   </TableBody>

Upvotes: 1

NeERAJ TK
NeERAJ TK

Reputation: 2695

In addition to the above answer by Drew Reese, Please remove responseData from useEffect params as you are using setResponseData inside it and useEffect gets triggered on every state change :

useEffect(() => {
    getServicesById()
      .then((res) => {
        setResponseData(res.data);
      })
      .catch((error) => {
        console.log(error);
      });
  }, [setResponseData]);

Upvotes: 1

Related Questions