changing react state according to firestore

36 views Asked by At

I am maintaining an array of DiaryEntries as a state and I want to update that list with Diary Entries stored in the firesore database. entries array gets printed correctly but the state does not get updated. what is wrong with my code?

diaryReducer.ts


interface DiaryEntry {
    title: string;
    description: string;
}


const diarySlice = createSlice({
    name: "diary",
    initialState: {
        entries: [],
        currentEntry: {title: "", description: ""}
    }as DiaryState ,
    reducers: {
        getEntry(){},
        getDiary(state,acion){
            state.entries = acion.payload;
        },

diarySaga.ts

function* addDiarySaga(action: any) {
    const { title, description } = action.payload;
    yield addDoc(diaryRef, { title, description });

}

function* getDiarySaga() {
    try{
        let entries:any = [];
        const unsubscribe = onSnapshot(diaryRef, (querySnapshot) => {
            querySnapshot.docs.forEach((doc)=>{
                let title = doc.data().title;
                let description = doc.data().description
                let diary= {
                    title: title,
                    description: description
                } 
                entries.push(diary)
            })
                
            });

        console.log(entries);
        yield put(getDiary(entries));
    

    }
    catch(error) {
        console.log(error);
    }
}

export function* diarySaga() {
    yield takeLatest("diary/addEntry", addDiarySaga);
    yield takeLatest("diary/getEntry", getDiarySaga);
}
1

There are 1 answers

0
Frank van Puffelen On

Data is loaded from Firestore (and most modern cloud APIs) asynchronously. To prevent blocking the browser/app during this time, the loading happens in the background and the main code continues to execute. This means that your yield put(getDiary(entries)) happens before any data is loaded.

The solution for this is always the same: any code that requires the data has to be *inside the onSnapshot callback that gets called when the data is available. So in your case: the call to put(getDiary(entries)) needs to be inside the callback.

So:

const unsubscribe = onSnapshot(diaryRef, (querySnapshot) => {
    querySnapshot.docs.forEach((doc)=>{
        let title = doc.data().title;
        let description = doc.data().description
        let diary= {
            title: title,
            description: description
        } 
        entries.push(diary)
    })            
    console.log(entries);
    put(getDiary(entries));
});

That probably means that you can't use yield on it, so you may have to figure out what you actually want to return.