I
I
Ivan2022-01-07 20:27:55
React
Ivan, 2022-01-07 20:27:55

How to execute an action only after dispatch of an asynchronous function?

Good afternoon.
I have the following function in a component:

function uploadFile(canvas, crop) {
    if (!crop || !canvas) {
      return;
    }
    canvas.toBlob(async (blob) => {
      const formData = new FormData();
      formData.append('file', blob);

      dispatch(updateAvatar({ file: formData, userId: user._id }));

      setIsOpen(false);
      setUpImg(null);
      setCompletedCrop(null);
    });
  }

where updateAvatar is
export const updateAvatar = createAsyncThunk(
  'user/updateAvatar',
  async ({ file, userId }, thunkApi) => {
    try {
      const response = await UserService.updateAvatar(file, userId);
      return response.data;

    } catch (err) {
...
}


I need to make sure that the code below dispatch only executes after the asynchronous action code has been executed.
If I just write the following code instead of the dispatch(updateAvatar({ file: formData, userId: user._id })) line in the function, then everything will work, but I will lose all the benefits of redux-toolkit and the component will become "dirty", I need this would not want to.
const response = await UserService.updateAvatar(formData);
dispatch(updateAvatar(response.data));


I tried the following:
in the component with the function above, I pass the props:
action={async (formData) => {
  return dispatch(updateAvatar({ file: formData, userId: user._id }));
}}


and I change the function like this:
function uploadFile(canvas, crop) {
    if (!crop || !canvas) {
      return;
    }
    canvas.toBlob(async (blob) => {
      const formData = new FormData();
      formData.append('file', blob);

      const test = await action(formData);

      if (test.meta.requestStatus !== 'pending') {
        setIsOpen(false);
        setUpImg(null);
        setCompletedCrop(null);
      }
    });
  }

and everything works, but it seems to me that this is some kind of nonsense. and what to do if in another case I can’t transfer such a props ... I can’t solve it like that anymore.
in general, I ask you to explain how I should proceed to solve the problem.
Thanks

Answer the question

In order to leave comments, you need to log in

2 answer(s)
G
gsaw, 2022-01-09
@gsaw

If this dispatch is from useReducer, then it only schedules some data changes that will only be applied in the next render cycle. Playing with async won't help. You need to use useEffect so that if some variable in the state changes, useEffect cleans up the variables.
For example

useEffect(() => { 
      setIsOpen(false);
      setUpImg(null);
      setCompletedCrop(null);
},[uploaded])

A
Alexander Smirnov, 2022-01-09
@sasha-hohloma

When working with React, any change in the interface must be reflected in the state. If you need to receive data asynchronously, then your component will have at least 3 states: data at initialization, waiting for loading, data received. Also, dispatch is always synchronous, which is why we need libraries like React Thunk that do lazy state changes. Therefore, writing asynchronous actions without calling dispatch is wrong.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question