skip to Main Content

I have a reducer for a state. When the reducer’s dispatch is called, I need to get the newly created item. Alternatively I need a callback for the dispatch, so I can perform further actions AFTER the dispatch has completed.

Here is my code:

useDays:

export default function useDays() {
    const days: Day[] = useContext( DaysContext );
    const daysDispatch: DaysDispatch = useContext( DaysDispatchContext );

    return {
        getDayByDate: ( date: Date ): Day => {
            //Search for a day by date
            let day = days.find( day => day.date.toDateString() == date.toDateString() );
            //If day with such date isn't found, create one.
            if ( !day ) {
                daysDispatch( {
                    type: 'create',
                    data: {
                        date: date
                    }
                } )
            }
            //Still can't find day after adding, presumably because the dispatch is async.
            day = days.find( day => day.date.toDateString() == date.toDateString() ); //Returns undefined
            return day;
        }
    }
}

daysReducer:

export default function daysReducer( all_days: Day[], action: Action ): Day[] {
    switch ( action.type ) {
        case 'create': {
            const new_all_days = [
                ...all_days,
                {
                    date: action.data.date,
                    note: action.data.note ? action.data.note : null
                }
            ]

            return new_all_days;
        }
    }
}

Where the dispatch is used:

const handleDayChange = ( e ) => {

    setCurrentDay( getDayByDate( e.target.value ) );

}

As you can see in the code, I have a function that gets a Day object. The key here is that if the Day object with the specified date is not found, one should be created. The current approach doesn’t work because dispatch is run asynchronously, so I can’t immediately retrieve the newly created Day object. One method could be to set a timeout for a few milliseconds, but I’m not sure if that’s the right way to go about it.

Thanks in advance!

2

Answers


  1. Chosen as BEST ANSWER

    SOLVED

    I managed to solve this myself. Basically what I needed to change:

    1. Read a ref of the days state instead of the state itself (not sure why, would be nice if someone explained. I'm guessing it's something to do with the fact that the state variable gets updated only after each render)
    2. Wrap the getDayByDate function in a promise, and set a timeout after a new day has been created, and resolve the promise after the timeout

    Here is the updated code:

    export default function useDays() {
    const days: Day[] = useContext( DaysContext );
    const daysDispatch: DaysDispatch = useContext( DaysDispatchContext );
    
    const all_days_ref = useRef( null );
    all_days_ref.current = days;
    
    
    return {
        getDayByDate: ( date: Date ): Promise<Day> => {
            return new Promise<Day>( ( resolve, reject ) => {
                let day = all_days_ref.current.find( day => day.date.toDateString() == date.toDateString() );
                if ( !day ) {
                    daysDispatch( {
                        type: 'create',
                        data: {
                            date: date
                        }
                    } );
    
                    setTimeout( () => {
    
                        day = all_days_ref.current.find( day => day.date.toDateString() == date.toDateString() );
    
                        resolve( day );
                    }, 200);
                } else {
                    resolve( day );
                }
    
            })
        }
    }
    }
    

  2. I want to see your action as well ,

    if you are not doing any asynchronous job then your code is correct ,
    after dispatch ,you will get value in reducer , console action there first .

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search