-1

In a component, I am trying to dispatch two different actions from two different reducers. They are updating different states in my application. Here is the partial code for the component:

    onSubmit = e => {
        e.preventDefault()
        const food = {
            date: this.state.date,
            mealCategory: this.state.mealCategory
        }
        this.props.addFood(food) //from mapDispatch to props updates the food state, viewing in redux tools shows proper state for food
        const meal = this.props.food //comes from mapStateToProps 
        console.log(meal, 'in form') //logging this shows it's not the updated state I am expecting
        this.props.addMeal(meal) //trying to update state from food reducer to the meal reducer, meal reducer is not updated via redux tools
    }
.
.
...

const mapDispatchToProps = (dispatch) => ({
    addFood: food => dispatch(addFood(food)),
    addMeal: meal => dispatch(addMeal(meal))
})

const mapStateToProps = state => ({
    food: state.food
})

export default connect(mapStateToProps, mapDispatchToProps)(AddFoodForm)

When I check redux tools my state for food shows correctly. However, adding that state to meal const meal = this.props.food within my function shows a state that is not updated along with the call to this.props.addFood(food) which dispatches an action from my food reducer. I am aware the update is asynchronous. How can I update state properly?

Just to clarify, my foodReducer takes care of fetches and through forms adds other properties to the state of foodReducer. Once the submit button is pressed, I have the state with all the properties I need. I want to take that final state and pass it to my mealsReducer. I can't use the food variable because that is just a subset of properties that I want on the state. Not sure, if I properly understood your suggestion.

My foodReducer case:

case 'ADD_FOOD':
            {
                const { date, mealCategory } = action.payload
                const itemDetails = {
                    date,
                    mealCategory
                }
                return { ...state, ...itemDetails }
            }

This brings about the final state that I want to add via the mealReducer case:

const mealReducer = (state = [], action) => {
    switch (action.type) {
        case 'ADD_MEAL': {
            console.log(action.payload);
            const meal  = action.payload
            console.log(meal);
            return [...state, meal]
        }
        default:
            return state
    }
}
Altaf
  • 399
  • 1
  • 5
  • 15
  • Does this answer your question? [Redux does not update state immediately](https://stackoverflow.com/questions/51247040/redux-does-not-update-state-immediately) – Emile Bergeron Jul 20 '20 at 02:40
  • Hi Emile, I suspected state was updated asynchronously, but I am unaware of how to call it with a callback, I've looked into redux thunk into returning a promise, but having a bit of difficulty writing the code to make it make sense. – Altaf Jul 20 '20 at 03:45
  • The render function will be called with the updated values, so you really don't need to worry about it in `onSubmit`. If adding a meal is really needed there, use the `food` variable directly, but honestly, this should be done in the store logic. – Emile Bergeron Jul 20 '20 at 03:47
  • Just to clarify, my foodReducer takes care of fetches and through forms adds other properties to the state of foodReducer. Once the submit button is pressed, I have the state with all the properties I need. I want to take that final state and pass it to my mealsReducer. I can't use the food variable because that is just a subset of properties that I want on the state. Not sure, if I properly understood your suggestion. – Altaf Jul 20 '20 at 04:00
  • If I was using setState, I know I would just use a callback to get the state after setting state, trying to accomplish the same thing via redux, and I am a bit stumped on how to accomplish that. – Altaf Jul 20 '20 at 04:02
  • 1
    _"foodReducer takes care of fetches"_ Reducers shouldn't have any side-effects, so that's something that should be moved to the store action logic. In that same `addFood` action logic where some API call is happening, on the response success, call your `addMeal` action directly from there. The React components should only care about rendering, they should not implement all your business logic. – Emile Bergeron Jul 20 '20 at 04:14
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/218179/discussion-between-altaf-and-emile-bergeron). – Altaf Jul 20 '20 at 04:25
  • 1
    Redux actions are synchronous. If you want asynchronous, use redux-thunk. The question is very unclear, but if you're wanting `const meal = this.props.food` to be the result from `this.props.addFood(food)`, you can't do that in the same render. `this.props.food` will be updated in the next render. It would make more sense to create a new redux action that combines the logic of the two actions. – Ryan Walker Jul 20 '20 at 04:44
  • Hi Ryan that is what I gathered from Emile, but where should that new redux action live, should it be in food or meal, I assume as long as I have it in either that I can call the actions from either reducer. Also, if I am using thunk I'll return the dispatch function that returns dispatch of two action calls. I assume this is what you mean? – Altaf Jul 20 '20 at 04:56
  • Pseudocode: `const somefunction = dispatch => return { dispatch addFood(food) const meal = store.getState().food dispatch addMeal(meal)` – Altaf Jul 20 '20 at 05:00

1 Answers1

0

Based on your comments I was able to come up with a solution. I imported the addFood action within the meal action file and used Thunk to get the state to call the addMeal action.

export const addFoodToMeal = (food) => {
    return (dispatch, getState) => {
        dispatch(addFood(food))
        const meal = getState().food
        return dispatch(addMeal(meal))
    }
}
Altaf
  • 399
  • 1
  • 5
  • 15