I am trying to perform a state change and after that I want to make an API request. But since useState is asyncronous, I cannot do that. I tried using the useEffect() hook but it is impossible to find the exact array element that was changed.
In toggleCompleted() I am getting the old value for updatedTask because setTasks is asyncronous.
App.js
import { useState, useEffect } from 'react'
const App = () => {
const [tasks, setTasks] = useState([])
const toggleCompleted = id => {
setTasks(
tasks.map(task => {
return task.id === id
? { ...task, isCompleted: !task.isCompleted }
: task
})
)
// This will get the old value
const updatedTask = tasks.find(task => task.id === id)
// PUT request here
fetch(`${api_url}/tasks/${id}`, {
method: 'PUT',
body: JSON.stringify(updatedTask)
})
}
}
export default App
I also tried using the useEffect hook, but I am getting the whole tasks array and not the individual task that was updated so I cannot use this to make a PUT request to my backend API.
useEffect(() => {
// unable to get the exact one task that was updated
}, [tasks])
Why not do it like this:
import { useState, useEffect } from 'react'
const App = () => {
const [tasks, setTasks] = useState([])
const toggleCompleted = id => {
let updatedTask = tasks.filter(task => task.id === id)
updatedTask.isCompleted = !updatedTask.isCompleted
// PUT request here
fetch(`${api_url}/tasks/${id}`, {
method: 'PUT',
body: JSON.stringify(updatedTask)
})
setState(tasks.map(task => task.id === id ? updatedTask : task))
}
}
export default App
This should solve your problem.
Basically, we capture the task we want to update, manipulate it however we want, then finally set the state.
I ended up refactoring my code to where I first get and update the element which is used both for updating the state and also for making the API call.
const toggleReminder = id => {
let updatedTask = tasks.find(task => task.id === id)
updatedTask.isCompleted = !updatedTask.isCompleted
setTasks(
tasks.map(task => {
return task.id === id
? updatedTask
: task
})
)
fetch(`${api_url}/tasks/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(updatedTask)
})
}
Try this one
import { useState, useEffect } from 'react'
const App = () => {
const [tasks, setTasks] = useState([])
const toggleCompleted = async (id) => {
const currentTask = tasks.find(task => task.id === id);
const updatedTask = {...currentTask, isCompleted: !currentTask.isCompleted};
await fetch(`${api_url}/tasks/${id}`, {
method: 'PUT',
body: JSON.stringify(updatedTask)
});
setTasks(tasks.map(task => task.id === id?updatedTask :task));
}
}
export default App
you can use normal function instead of async / await and put the setTasks in .then but I think it's simpler.