May
May

Reputation: 61

Update a Table with React Hooks when a Row is Added, Deleted and Modified? [Issue: Gets Before Post and Delete]

I'm using Axios to get, put, and delete values from our database and have them displayed in a table; however, I need to refresh the page to see my changes. To find answers, I've visited these posts: How to update the page after call Axios Successful ? React, refresh table after action in react, and How can I use React Hooks to refresh a table when a row is deleted or added? Unfortunately, I am still stuck and unsure how to dynamically update table rows upon response updates.

Update: I have noticed that the getValues function runs prior to the post and delete methods, which is why it is currently showing the previous values before the methods execute.

Axios.js - Where I am using get and delete methods. They work as I've had responses printed on the console.

import axios from "axios";

const getValues = async () => {
  const values = await axios
    .get("https://pokeapi.co/api/v2/type/")
    .then((response) => {
      return response.data;
    })
    .catch(function (error) {
      console.log(error);
    });
  return values;
};


const postValues = (values) => {
  axios
    .post("https://pokeapi.co/api/v2/type/")
    .then((response) => {
      console.log("Post Values: ", response.data);
      return response.data;
    });
};

const deleteValues = (id) => {
  console.log(id);
  const deleteValues = axios
    .delete(`https://pokeapi.co/api/v2/type/${id}`)
    .then((response) => {
      console.log("Delete Values: ", response);
    })
    .catch(function (error) {
      console.log(error);
    });
  return deleteValues;
};

export { getValues, postValues, deleteValues }

ValuesTable.js - Where the delete method executes

  import Axios from "./Axios";

  const [data, setData] = React.useState();

  useEffect(() => {
    Axios.getValues().then((result) => {
      setData(result.data);
    });
  }, [data]);

  return (
  {data.map((values) => {
    <TableRow/>
    <TableCell>{values.values}</TableCell>
    <TableCell>
      <Button
        onClick={() =>
           Axios.deleteValues(values.id);
        }
      />
    })};  
  )

Form.js - Where the post method executes

if (values.id === 0) {
  Axios.postValues(values);
} else {
  Axios.putValues(values, values.id);
}

UseState setData(result.data) loads all the existing values in the database. Method deleteValues deletes a value in an array. Method postValues adds a value into the database.

Upvotes: 1

Views: 8001

Answers (4)

Drew Reese
Drew Reese

Reputation: 202686

Well, you don't what to unconditionally call setData within an useEffect hook with data as a dependency as this will cause an infinite loop (render looping) to occur.

Since the getValues utility already unpacks the response.data value there is likely no need to do it again in your UI. Also, remove the data dependency.

useEffect(() => {
  Axios.getValues()
    .then((result) => {
      setData(result.results);
    });
}, []);

For the deleteValues utility, if console.log("Delete Values: ", response); is showing the correct values than I think you need to return this value from deleteValues.

const deleteValues = (id) => {
  console.log(id);
  const deleteValues = axios
    .delete("https://pokeapi.co/api/v2/type/${id}`)
    .then((response) => {
      console.log("Delete Values: ", response);
      return response; // <-- new data values
    })
    .catch(function (error) {
      console.log(error);
    });
  return deleteValues;
};

Then in ValuesTable you need to update your data state with the new deleted values.

{data.map((values) => {
  ...
    <Button
      onClick={() => {
        Axios.deleteValues(values.id)
          .then(data => setData(data));
      }}
    />
  ...
})};

Update

Ok, since the deleteValues utility doesn't return the updated data from the backend you will need to maintain your local state manually. I suggest doing this work in a callback handler. Upon successful deletion, update the local state.

const [data, setData] = React.useState();

useEffect(() => {
  Axios.getValues().then((result) => {
    setData(result.data);
  });
}, []);

const deleteHandler = id => async () => {
  try {
    await Axios.deleteValues(id); // no error, assume success
    setData(data => data.filter((item) => item.id !== id));
  } catch(err) {
    // whatever you want to do with error
  }
};

return (
  ...
  {data.map((values) => {
    <TableRow/>
    <TableCell>{values.values}</TableCell>
    <TableCell>
      <Button onClick={deleteHandler(values.id)}>
        Delete
      </Button>
    })}; 
  ...
)

Note that I've written deleteHandler to be a curried function so you don't need an anonymous callback function for the button's onClick handler. It encloses the current id in an "instance" of the callback.

Update 2

If you are making a lot of different changes to your data in the backend it may just be easier to use a "fetch" state trigger to just refetch ("get") your data after each backend update. Anytime you make a call to update data in your DB, upon success trigger the fetch/refetch via a useEffect hook.

const [data, setData] = React.useState();
const [fetchData, setFetchData] = useState(true);

const triggerDataFetch = () => setFetchData(t => !t);

useEffect(() => {
  Axios.getValues().then((result) => {
    setData(result.data);
  });
}, [fetchData]);

const deleteHandler = id => async () => {
  try {
    await Axios.deleteValues(id); // no error, assume success
    triggerDataFetch(); // <-- trigger refetch
  } catch(err) {
    // whatever you want to do with error
  }
};

Upvotes: 2

May
May

Reputation: 61

Since the deleteValues function deletes a specific object from the array on the server-side, I have decided to filter the list of objects in the array in order to remove the matching id to reflect on the front end. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

This is how I approached it.

{data.map((values) => {
  ...
    <Button
      onClick={() => {

        setData(data.filter((item) => item.id !== values.id)); // <- Filter

        Axios.deleteValues(values.id)
          .then(data => setData(data));
      }}
    />
  ...
})};

Upvotes: 0

Piyush Rana
Piyush Rana

Reputation: 667

import axios from "axios";  const getValues = async () => {  const values = await axios .get("https://pokeapi.co/api/v2/type/")

.then((response) => { return response.data; }) .catch(function (error) { console.log(error); }); return values; };

I don't know why but what are you trying to achieve with this. You should either use async/await clause or then clause but you are using both atleast have some good practice of coding first. Second I think you should use async await inside try catch and remove then/catch phrases to make your code more understandable, then if you store your result inside values then simply return values.data and your problem might be resolved.

Upvotes: 0

edhi.uchiha
edhi.uchiha

Reputation: 364

I think you wrong in here :

useEffect(() => {
    Axios.getValues().then((result) => {
      setData(result.data); // result has no data property
    });
  }, [data]);

Please try change to this

useEffect(() => {
    Axios.getValues().then((result) => {
      console.log("RESULT",result); // check the actually response from API
      setData(result.results); // the response of data is called results
    });
  }, [data]);

Upvotes: 0

Related Questions