Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update Recoil state from async function

I've got an application which performs API actions when buttons are hit. When the action is ended I update a Recoil state. But the thing is when I try to update the state I replace the old one with the new updated one, and in an async context I don't know how to get the current state at the moment where my code is executed.

const [tasks, setTasks] = useRecoilState(tasksState);

const handleAction = async (task: Task): Promise<void> => {
  try {
    // some async stuff here with an API

    // update the recoil state here
    // MY ISSUE HERE is that the "tasks" state is not the one at the moment where the code is executed after the API response, 
    // but at the moment where handleAction has been called...
    // So I can override the state with old values, previously updated from an other handleAction ended earlier.
    const newTasks = updateTasks(tasks, anOtherParameterFromApiResponse);
    setTasks(newTasks);
  } catch(error){
    console.log("error: ", error);
  }
}

Can you please me how to approach such pattern and be able to update the state when my async actions are ended.

Note: my state is an array of objects, and my updateTasks() function is a function which update an object from this array so I can update the state with this computed array.

Thank you in advance for the precious help you will give me!

like image 706
Antoine Avatar asked Dec 06 '25 02:12

Antoine


1 Answers

I've find a solution by self :

I've created a 'selector' from my tasks 'atom', and delegated in a custom 'set' method the aggreation of the new object with the object array state. Thanks to the 'get' method provided in parameter I can access to the object array state up to date.

selectors.ts :

/**
 * Allow to update the tasksState by passing in parameter the task to update
 * That way we can call this from an async context and updating value by agreagating to the eventual new ones
 */
export const taskUnitUpdaterSelector = selector<Task>({
  key: "taskUnitUpdaterSelector",
  get: () => {
    throw new Error("Not implemented !");
  },
  set: ({ get, set }, newTask: Docker) => {
    const tasks = get(tasksState);
    // remove from tasks list the new one updated to add it then
    const tasksCloneWithoutNewUpdatedOne = tasks.filter(
    (t) => !(t.name === newTask.name && t.server === newTask.server),
  );
  const newTasks = [...tasksCloneWithoutNewUpdatedOne , newTask];
  set(tasksState , newTasks);
});

component.ts

const taskUnitUpdater = useSetRecoilState(taskUnitUpdaterSelector);

const handleAction = async (task: Task): Promise<void> => {
  try {
    // some async stuff here with an API

    // the new tasks array is computed inside the selector set function to be able to access to up to date data !
    taskUnitUpdater(newTask );
  } catch(error){
    console.log("error: ", error);
  }
}
like image 186
Antoine Avatar answered Dec 07 '25 16:12

Antoine