I have a thunk which fetches data from firestore and maps doc IDs to each doc. For some reason it's fulfilled and returns undefined
before the mapping finishes. I can verify this because the log before return statement appears a second or two after the fulfilled reducer logs.
My thunk:
export const fetchNotesByCustomerId = createAsyncThunk(
'fetchNotesByCustomerId',
async (custId, { getState, dispatch }) => {
const customerId = custId;
if (customerId !== undefined) {
notesService
.fetchNotesByCustomerId(customerId)
.then((snapshot) => {
if (snapshot.docs.length !== 0) {
const notes = snapshot.docs.map(
(doc) =>
({
...doc.data(),
docId: doc.id
} as INote)
);
console.log('notes inside thunk: ', notes); // This shows after the fulfilled reducer logs
return {
error: false,
data: notes
};
} else
return {
error: true,
message: 'No notes found in firebase',
data: []
};
})
.catch((error) => {
return { error: true, message: error.message, data: [] };
});
} else return { error: true, message: 'No Customer Id' };
}
);
I know .map
is an async function that returns an array of promises, but when I use await
, intellisense notifies me that it makes no difference on the behavior of the function.
So as an alternative, I tried to resolve the array of promises like this, but saw no difference:
.then(async (snapshot) => {
if (snapshot.docs.length !== 0) {
const notesPromisesArray = snapshot.docs.map(
(doc) =>
({
...doc.data(),
docId: doc.id
} as INote)
);
await Promise.all(notesPromisesArray).then((notes) => {
console.log('notes inside thunk: ', notes);
return {
error: false,
data: notes
};
});
} else
return {
error: true,
message: 'No notes found in firebase',
data: []
};
})
How can I get this .map
to return before the thunk is fulfilled?
The problem you're facing has nothing to do with Redux Async Thunk but basic JavaScript only.
In the if condition that you have:
if (customerId !== undefined) {
notesService
.fetchNotesByCustomerId(customerId)
.then((snapshot) => {
if (snapshot.docs.length !== 0) {
const notes = snapshot.docs.map(
(doc) =>
({
...doc.data(),
docId: doc.id,
} as INote)
)
console.log('notes inside thunk: ', notes) // This shows after the fulfilled reducer logs
return {
error: false,
data: notes,
}
} else
return {
error: true,
message: 'No notes found in firebase',
data: [],
}
})
.catch((error) => {
return { error: true, message: error.message, data: [] }
})
}
You're using noteService.fetchNotesByCustomerId()
which is an async function.
When the execution goes to this block, since JavaScript Event loop will forward the async function to its thread pool, it goes to the next step without even the execution of noteService.fetchNotesByCustomerId()
getting over and resolves the thunk without returning anything.
You can easily resolve this by adding a return statement next to your call:
export const fetchNotesByCustomerId = createAsyncThunk('fetchNotesByCustomerId', async (custId, { getState, dispatch }) => {
const customerId = custId
if (customerId !== undefined) {
return notesService
.fetchNotesByCustomerId(customerId)
.then((snapshot) => {
if (snapshot.docs.length !== 0) {
const notes = snapshot.docs.map(
(doc) =>
({
...doc.data(),
docId: doc.id,
} as INote)
)
console.log('notes inside thunk: ', notes) // This shows after the fulfilled reducer logs
return {
error: false,
data: notes,
}
} else
return {
error: true,
message: 'No notes found in firebase',
data: [],
}
})
.catch((error) => {
return { error: true, message: error.message, data: [] }
})
} else return { error: true, message: 'No Customer Id' }
})