DEV Community

Prateik Lohani
Prateik Lohani

Posted on

useEffect() or Event handler?

Hi there!
I just wanted some advice here. Basically I have a To-Do list application that uses a Postgres database and an express backend. I just have one table that stores the name, id, whether completed or not, and a note related to the task in it.

An image of my To-Do list application main (and only, lol) page

My doubt is whether I should use a useEffect() or just plain old event handlers for this.

This is how I have implemented my functionality:

  1. When a user clicks on the add task button, the event handler- handleAddTask() fires up -> adds the task to the database -> then fetches the data once again to get the updated data.

NOTE- I am fetching the data again and again since I thought it wouldn't be too bad for an application that is this small (and also because I don't yet know how to do this in a more efficient way🥲).

  1. Similar to the above, when the user deletes a task, a database DELETE request is sent and all the tasks are fetched again to keep the frontend in sync with backend.

  2. Same for editing a task too. Edit it, PUT request goes, and all tasks fetched again.

The handleAddTask() event handler:


 javascript
function handleAddTask(title){
        if (title.trim() === ""){
            alert("You cannot add empty task!");
        }
        else{
            controller.addTask(title)
            .then(() => controller.fetchData())
            .then((todos) => {
                console.log("Got your todos!:", todos);
                dispatch({
                    type: "set",
                    todos: todos,
                })
                handleSetTasksRemaining(todos);
            });

            // clearing the task input bar upon adding the task
            setTitle("");
        }
    }


Enter fullscreen mode Exit fullscreen mode

The handleDeleteTask() event handler:


 javascript
function handleDeleteTask(id){
        controller.deleteTask(id)
        .then(() => controller.fetchData())
        .then((todos) => {
            console.log("Got your todos!:", todos);
            flushSync(() => {
                dispatch({
                    type: "set",
                    todos: todos,
                });
            });
            handleSetTasksRemaining(todos);
        })
    }


Enter fullscreen mode Exit fullscreen mode

And finally the handleEditTask() event handler:


 javascript
function handleEditTask(id, title, completed, description, showEditButton){
        setEditInput(showEditButton);

        if(title.trim() === ""){
            alert("You cannot add empty task!");
        } else{
            controller.updateTask(id, title, completed, description === "" ? null : description)
            .then(() => controller.fetchData())
            .then((todos) => {
                console.log("Got your todos!:", todos);
                dispatch({
                    type: "set",
                    todos: todos,
                })
                handleSetTasksRemaining(todos);
            })
        }
    }


Enter fullscreen mode Exit fullscreen mode

I am really confused about what I should do since everyone keeps saying that data fetching should be done in useEffect. But in my case the data should only be added, deleted or edited whenever I click on an appropriate button.

Is this a bad approach? What is the better approach if any? Please help.

Top comments (10)

Collapse
 
miketalbot profile image
Mike Talbot ⭐

Firstly, if you retain the event handler approach, you should probably extract the repeated code in those functions into a "getAllTodos" function you can call from each button handler to keep it DRY and have the purpose of each routine be clearer.

A useEffect could centralise that logic and fetch the data when its dependencies change. The dependency could then be a simple incrementing integer. You could potentially then manage an AbortController in future to ensure that outdated requests are aborted should changes come in quick succession.

Collapse
 
sanskari_patrick07 profile image
Prateik Lohani

yeah but there's also edits to the note and title as well. If i were to change them, the count of the tasks will not change, so an incrementing integer cannot work as a dependency in that case.

Collapse
 
miketalbot profile image
Mike Talbot ⭐

You just need a refresh count to tell it to refetch the data, it's not the number of rows:


      function useRefresh() {
           const [refresh, setRefresh] = useState(0)
           const doRefresh = useCallback(()=>setRefresh(r=>r+1), [])
           doRefresh.id = refresh
           return doRefresh
      }

      function ToDos() {
           const refresh = useRefresh()
           useEffect(()=>{
                getTheData()
           }, [refresh.id])

          return <>...</>

          function modifySomething() {
                   // do modification
                 refresh()
          }

          function addSomething() {
                 // add a thing
                 refresh()
          }


      }




Enter fullscreen mode Exit fullscreen mode
Collapse
 
cookiemonsterdev profile image
Mykhailo Toporkov 🇺🇦

Many people forget that react is just a lib to manage dom in efficient way It does not dictate how to fetch data from server.

In general we are using react with routers libs or frameworks like remix (using react router dom) or next.js that has file based routing. These packages also contains predefined util for endpoint revalidation (refetch if you like). So the best practice is to use these utils inside function that handle request for any change.

Certainly, there are places where it is impossible to avoid using useEffect for fetching, however I would try to reduce its usage as much as possible.

Collapse
 
quassain profile image
Quassain raza

data fetching varies to situation to situation or what your usecase is, normally it is recommended that useEffect is better hook for side effects and data fetching related to component lifecycle like you want to sync data in some array from any Api when component mounts or unmounts, you can also do data fetching on event handlers as well but in your usecase it is not the most efficient approach. Fetching data twice can lead to unnecessary network requests and increased latency. you can update the local state immediately after the task is added, and then optionally verify or synchronize with the server in the background.

Collapse
 
rw3iss profile image
Ryan Weiss

Try to keep the lines of infrastructure discrete and separated, as a general guide in application programming.

If your front end client framework is React, you should and will most likely need to rely on useEffect for the view-layer UI bindings and changes. The useEffect can rely on any other infrastructure that you might need, including your custom services.

In your case, you should use useEffect, or write your own useData hook, at the beginning of the component/page. It should fetch the data and bind it it the view using useState, and your local page view component should rely on and display that state.

If you want to integrate custom events in your application, it is generally a good idea to register them in a global app Context (using useContext), and have the events received there change local app state within the Context, which will get reflected in other various components utilizing it through useContext, whereby those components would register the dependency in a useEffect as well. Of course the component/page itself could register and listen to events in the same way without a global context, if it's only a single page/purpose app.

You can also have individual stores that handle those kinds of things, and register for events or update local data, then have your pages and components bind to its state changes through useEffect.

Collapse
 
dipanjan profile image
Dipanjan Ghosal

If you analyse your code, you'll see that you're repeating the same fetch code and it's chain in multiple places. I'd suggest you to write all that code just once in a useEffect to make it DRY.
Now, how to trigger the useEffect? A quick and dirty way is to add a boolean variable as useEffect dependency.
Now whenever you're adding or deleting tasks, make the request to server, and then on success, just toggle the boolean variable's value to trigger the useEffect, thus triggering a resync.

If you don't like this, at least put the resync code in a function and call that function after every successful request.

Collapse
 
thoughtworkstcaceres profile image
Tyler Caceres

react.dev/learn/you-might-not-need...
I strongly recommend reading through this doc. This will answer many questions you may have in general about needing/not needing useEffect.

Collapse
 
nonfungiblehayor profile image
EIbrahim Ayodeji

You can create a context state value that holds the data from your API, so anytime you add a new task the state context is updated with the data from your api

Collapse
 
snehaltayde profile image
Snehal Tayde

Hi I have created an Video where I used UseEffect for fetching todos from the database, why do you feel, using useEfect is not a good idea?

youtube.com/watch?v=QFaJ7KYhfh4