Home > OS >  How to get React to re fetch my data from API after a POST or DELETE to Database?
How to get React to re fetch my data from API after a POST or DELETE to Database?

Time:09-18

New to react and would appreciate any help with getting my components to re render data after a POST or DELETE. The data is flowing all the way through to my database on post & delete however I manually have to re load the page to have the updated dispaly

DELETE - State [data,setData] being passed in from useFetch custom hook

function BookList({ data, isPending, setData }) {
 

    if (isPending){
        return <p>Data Loading!</p>
    }
   

    const handleDelete = (book_id) => {
        fetch(`http://localhost:3000/books/${book_id}`, {
            method: 'DELETE'
        })
        .then(response => response.json())
        .then(()=> {
            const updatedBooks = data.data.filter(item => item.book_id !== book_id)
            setData(updatedBooks)
           
        })

    }
   
  const rows = data.data.map(({book_id, book_title, author}, index) => (
    <tr>
        <td>{book_id}
        </td>
        <td>{book_title}</td>
        <td>{author}</td>
        <button onClick={()=> handleDelete(book_id)}>Delete</button>

    
    </tr>
  ))

  return (
    <div>
      <table>
        <thead>
          <tr>
            <th>Id</th>
            <th>Title</th>
            <th>Author</th>
            <th>Delete</th>
          </tr>
        </thead>
        <tbody>{rows}
        </tbody>
      </table>
    </div>
  );
}

export default BookList;

and POST

import { useState } from "react";

function NewBook({data, setData}) {
  const initialFormState = {
    book_title: "",
    author: ""
  };

  const [formData, setFormData] = useState({ ...initialFormState });

  const handleChange = ({ target }) => {
    setFormData({ ...formData, [target.name]: target.value });
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    const newBook = formData;
    console.log(newBook);
    const url = "http://localhost:3000/books";
    const requestOptions = {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(newBook),
    };
    fetch(url, requestOptions)
      .then((response) => console.log("Submitted Successfully"))
      .then(()=> {setData.push(formData)})
      .catch((error) => console.log("Form submit error", error));

    setFormData({ ...initialFormState });
   
  };

CodePudding user response:

Consider using global state management as indicated in the answer above. I suggest using react's context API, as it is beginner friendly and will serve you well for such a small project. Context API docs

Your context provider file could be something like this

import { createContext, useContext, useState } from "react";

const BooksContext = createContext();

export function useBooks() {
  return useContext(BooksContext);
}

export function BooksContextProvier({ children }) {
  const [bookList, setBookList] = useState([]);
  const [loading, setLoading] = useState(false);

  const value = {
    bookList,
    loading,
    setBookList,
    setLoading,
  };

  return (
    <BooksContext.Provider value={value}>{children}</BooksContext.Provider>
  );
}

Ensure to bring in your context in App.js and wrap it around your components like so

return (
    <BooksContextProvier>
        <NewBook />
        <BookList />
    </BooksContextProvier>
)

In NewBook, bring in the bookList and setBookList function from the context so that whatever change you make in the component will be reflected globally. So instead of calling setFormData again at the end of handleSubmit, you want to setBookList, and include the new book. So the code for your NewBook component will be something like this

  function NewBook() {
    const initialFormState = {
      book_title: "",
      author: ""
    };
  
    const [formData, setFormData] = useState({ ...initialFormState });
    const {bookList, setBookList} = useBooks()
  
    const handleChange = ({ target }) => {
      setFormData({ ...formData, [target.name]: target.value });
    };
  
    const handleSubmit = (event) => {
      event.preventDefault();
      const newBook = formData;
      console.log(newBook);
      const url = "http://localhost:3000/books";
      const requestOptions = {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(newBook),
      };
      fetch(url, requestOptions)
        .then((response) => console.log("Submitted Successfully"))
        .catch((error) => console.log("Form submit error", error));
  
      setBookList([formData, ...bookList])
     
    };
}

For your BookList compoent, you could incorporate the following code to take advantage of the context

function BookList() {
    const {bookList, setBookList} = useBooks()
 
    if (loading){
        return <p>Data Loading!</p>
    }
   

    const handleDelete = (book_id) => {
        fetch(`http://localhost:3000/books/${book_id}`, {
            method: 'DELETE'
        })
        .then(response => response.json())
        .then(()=> {
            const updatedBooks = data.data.filter(item => item.book_id !== book_id)
            setBookList(updatedBooks)
        })

    }

You can modify and utilize the loading state at global level as well, I have not included it in the sample code, but you can pull it into your components and manipulate it the same way you would the bookList.

CodePudding user response:

I think you should use state management tools such as react redux or provider to have access to global state all around the app. personally I prefer provider. however, redux has its own advantages. After choosing your state management approach, you can define a function to call API whenever the user has an interaction on a specific page or etc.

CodePudding user response:

There are many ways to manage state and as per the need. You need to choose what you need. I will start with the easiest way to manage your situation,

1. Lift State

As you have different related components and they each handle some operations which changes the state. State lifting means, you move all your states to a parent component and pass down the props [properties or events] needed for your child components.

In your case you will have a Parent component where you will fetch the data and your BookList and NewBook components will be it's children. You will move your delete logic to the parent and pass a function to BookList as props to invoke there.

Now when you Add/Remove you have the full book list at your disposal in the parent. You can delete a book and based on the status remove the item from your list or do an optimistic update of your list.

Optimistic update means, you immediate update your list and then based on the status of delete/add handle the list.

2. Context API

This is when you need to share some data globally with your APP like a theme data or auth data etc. You can handle your situation with context with you wrap your context provider with the common parent of BookList or NewBook. In your case lifting the state is easy.

3. Global State managers

if you want to manage your state in a central location you can use some state management tools like Redux or Mobx. This is one of the best approach if your app has some degree of complexity to manage state. Setting up redux is a bit intimidating but worth the effort.

  • Related