'Redux Toolkit: can I use createAsycThunk with Firebase listener functions (e.g. firestore.collection.onSnapshot)
Is there a way to use createAsyncThunk with Firebase listeners, for example firestore.collection.onSnapshot?
It may not work because the way onSnapshot works (it's a listener that receives data and fires a callback every time firestore updates and it returns a function that unsubscribes the listener). I tried implementing createAsyncThunk but couldn't figure it out.
Here's my current thunk implementation which does work:
const listenerUnsubscribeList = [];
export function fetchProjects() {
return dispatch => {
dispatch(fetchProjectsPending());
const unsubscribe = firestore
.collection('projects')
.onSnapshot(
snapshot => { dispatch(fetchProjectsFulfilled(snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })))) },
error => { dispatch(fetchProjectsError(error)) },
);
listenerUnsubscribeList.push(unsubscribe);
}
}
Here's my attempt at createAsyncThunk which does not work. I'm getting database/fetchProjects/pending
and database/fetchProjects/fulfilled
but payload is undefined
const listenerUnsubscribeList = [];
export const fetchProjects = createAsyncThunk(
'database/fetchProjects',
async (_, thunkAPI) => {
const unsubscribe = await firestore
.collection('projects')
.onSnapshot(
snapshot => { return snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })) },
error => { return error },
);
listenerUnsubscribeList.push(unsubscribe);
}
);
Solution 1:[1]
I tried making it work with createAsyncThunk
but did not manage to solve it.
I dont know if my approach is the right one but I simply created a custom thunk
that handles re-firing any action as needed. I create a loading
/ fulfilled
/ error
action that will handle the data accordingly.
// Action.js
/*
listenToInboxInDb is a firebase listener that will re-fire with fresh data.
See how I am returning unsubscribe to be able to call unsubscribe
when my component unmounts
*/
export const getConnectionRequests = () => {
return (dispatch) => {
dispatch(fetchConnectionsRequestsLoading())
const unsubscribe = listenToInboxInDb(([data, error]) => {
if (data) {
dispatch(fetchConnectionsRequestFullfilled(data))
}
if (error) {
dispatch(fetchConnectionsRequestRejected(error))
}
})
return unsubscribe
}
}
// Reducer.js
const connectionsSlice = createSlice({
name: "connections",
initialState: INITIAL_STATE,
reducers: {
// ...other reducers
fetchConnectionsRequestsLoading(state) {
state.connectionRequestsStatus = REQUEST_STATUS.loading
},
fetchConnectionsRequestFullfilled(state, action) {
state.error = null
state.data.connectionRequests = action.payload
state.connectionRequestsStatus = REQUEST_STATUS.succeeded
},
fetchConnectionsRequestRejected(state, action) {
state.error = action.error
state.connectionRequestsStatus = REQUEST_STATUS.failed
},
},
})
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Walter Monecke |