Redux-Toolkit createAsyncThunk request to firestore returns undefined

Viewed 56

I am learning react and have started on a personal project using redux, redux-toolkit and firestore to store data. I have a problem with fetching and storing data that I cannot solve. Its entirely possible that I'm using a wrong approach to the problem or I've forced myself to a dead-end from a technical perspective (I am a noob after all), so any suggestions are welcome. On to the matter at hand.

Everything I have made so far works, in the sense that firestore receives the request and stores data as requested, however the code is not technically correct as you will see in the snippets bellow. I've been using createAsyncThunk from redux toolkit to handle requests and it seems to be working, but something seems a bit off in the code. If I use setDoc(), for example, to update a field in firestore, the return value is undefined (i.e. Promise<void>), as per the documentation: https://firebase.google.com/docs/reference/js/firestore_.md#updatedoc and I'm not sure how to handle it. Bellow I have shared the snippets required for making an update on the users document.

Initialize Firebase and define an update function (FIREBASE__CONFIG omitted):

const app = initializeApp(FIREBASE_CONFIG);
const dbFB = getFirestore(app);

export const updateFB = async (url, id, values) => {
    const docRef = doc(dbFB, url, id);
    const docResponse = await updateDoc(docRef, values);
    
    return docResponse;
};

Define user slice (omitting configureStore here for brevity):

const initialState = {
    isLoggedIn: false,
    userDetails: null,
};

const userSlice = createSlice({
    name: 'user',
    initialState,
    reducers: {
        ...
    },
    extraReducers: (builder) => {
    builder.addCase(userUpdate.fulfilled, (state, action) => {
      state.userDetails = { ...state.userDetails, ...action.payload };
    });
    }
});

Define userUpdate action (the requestError dispatch is an update for another slice):

export const userUpdate = createAsyncThunk(
    'user/userUpdate',
    async ({ userId, values }, { dispatch }) => {
        try {
            const usersRes = await updateFB('/users', userId, values);      
            return usersRes;
        } catch (err) {
            console.log(err);
            dispatch(httpActions.requestError({ errorMessage: err.message || 'Something went wrong!'}));
        }
    }
);

Calling the update request on submit (using FormIK):

<Formik
    ...
    onSubmit={async (values, { setSubmitting }) => {
        dispatch(userUpdate({userId: userDetails.userId, values})).then(() => {
            setSubmitting(false);
            history.push(MY_PROFILE.path);
        });
    }}
>
    {({ isSubmitting }) => (
        <Form>
            <div className='form-field'>
            ...
            </div>
        </Form>
    )}
</Formik>

As you can see, I would like to continue execution of some other commands after the request has resolved on form submission. However, the return value of this request is undefined and it feels off to use .then() on it, even though it works. Perhaps I've gone completely off the beaten path? Help is appreciated, thank you :)

1 Answers

As you mentioned, the documentation stated that it will return Promise<void>. The reason behind: This is just a simple document write operation sent off to the server. If it did return the document data in the call, not only you'll need to wait for the data to be sent back, and you'll also be billed for a document read and bandwidth that is never needed.

If you need the updated data, you'll need to use getDoc() in a separate call. If you update multiple documents, you may need multiple getDoc() calls too. e.g:

export const updateFB = async (url, id, values) => {
    const docRef = doc(dbFB, url, id);
    const docResponse = await updateDoc(docRef, values);

    // Returns updated document.
    const docSnap = await getDoc(docRef);
    console.log(docSnap.data());

    return docSnap.data();
};
Related