Antoine
Antoine

Reputation: 560

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!

Upvotes: 3

Views: 3938

Answers (1)

Antoine
Antoine

Reputation: 560

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);
  }
}

Upvotes: 4

Related Questions