0

I have a page where I can see the detail of a recipe. Im grabbing all the data for the recipe from my store for example store.recipes.recipeDetail. In my RecipeDetail component, I use useEffect to fetch the data on component load by dispatching an action.

export default function RecipeDetail() {
    // Hooks
    const params = useParams()
    const dispatch = useDispatch()

    // Get redux state
    const recipeDetail = useSelector((store) => store.recipes.recipeDetail)

    // Reset store and fetch data on page load
    useEffect(()=>{
        dispatch(recipesActions.fetchRecipeAction(params.slug))
    }, [dispatch, params])

My redux looks something like this (I'm using Redux Toolkit)

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import * as API from 'services/api'

// Async actions
const fetchRecipeAction = createAsyncThunk(
    'recipes/fetchRecipe',
    async (slug, thunkAPI) => {
        const response = await API.fetchRecipe(slug)
        return response.data
    }
)


const initialState = {
    recipeDetail: null
}

const coreSlice = createSlice({
        name: 'recipes',

        initialState: initialState,

        reducers: {
        },

        extraReducers: {
            [fetchRecipeAction.fulfilled]: (state, action) => {
                state.recipeDetail = action.payload
            }
        }
    }
)


// Exports actions, asyncActions and reducer
export { fetchRecipeAction }
export default coreSlice.reducer

Now inside that Recipe Detail page, I have a link to "other recipes you might like", when I click on a recipe, a new recipe opens but the old recipe is still visible until the request to the backend is done.

I thought I could create a new action to reset the info inside de redux store:

reducers: {
    resetRecipeDetail: (state, action) => {
        state.recipeDetail = initialState.recipeDetail
    }
}

And then inside my component, I dispatch this action on component unload:

// Reset recipeDetail store on component unload
useEffect(()=>()=>{
    dispatch(stratsActions.resetStratDetail())
}, [dispatch])

But this action doesn't get dispatched when switching from one recipe to another one, so the store is not reset. (im guessing because the route doesnt change, only the recipe slug)

Is this the correct method? If so how can I make it work so it resets the store correctly? Is there a better method?

Many thanks

UPDATE: I've been able to dispatch the "resetRecipeDetail" action from within the fetchRecipeAction async action so anytime I fetch a new recipe the store will be cleared automatically. Not sure if this is the best method. What do you think?

const fetchRecipeAction = createAsyncThunk(
    'recipes/fetchRecipe',
    async (slug, thunkAPI) => {
        thunkAPI.dispatch(coreSlice.actions.resetRecipeDetail())    <-----ADDED THIS
        const response = await API.fetchRecipe(slug)
        return response.data
    }
)
Alvaro Bataller
  • 487
  • 8
  • 29
  • 1
    Looks like `RecipeDetail` is rendered in a `Router`. I see you include the `params` in the `useEffect` dependency. The "unload" action doesn't work because the component never unmounts so long as it stays on the same matched path and accepts different params. I'm not sure of the exact pattern when using redux-toolkit, but your async action normally would immediately dispatch a "loading" action that you can cue your UI from. You may be able to dispatch your clear recipe state action at the same place. – Drew Reese Jan 26 '21 at 09:59
  • this post is already answered [here](https://stackoverflow.com/questions/35622588/how-to-reset-the-state-of-a-redux-store). – DevAddict Jan 26 '21 at 10:02
  • @AriaMAN That seems to address a *slightly* different question. I think OP is looking to only clear *some* state, i.e. `recipesDetail`, not the entire app state, and having issue with where to dispatch the action to do it. But perhaps I've misunderstood. – Drew Reese Jan 26 '21 at 10:28
  • 1
    @DrewReese I've updated my code based on your suggestion. Let me know if this is what you were thinking about. – Alvaro Bataller Jan 26 '21 at 18:07
  • @AlvaroBataller Yes, something like that. Again, I'm rather unfamiliar with the Redux-toolkit patterns so it might not be quite the same pattern/solution, but logically should be equivalent. Is that change not clearing out the recipe state while the next recipe is fetched? Do you have the redux-dev-tool extension installed in your browser, is the clearing action dispatched and can you see your state update? – Drew Reese Jan 26 '21 at 19:08
  • 1
    In my opinion, a better structure for this app would be to separate the data about recipe details from the state of which recipe is the "current" one. I would have a `recipes` slice which stores all loaded recipes keyed by their `id` or `slug`. Look into `createEntityAdapter`: https://redux-toolkit.js.org/api/createEntityAdapter – Linda Paiste Jan 26 '21 at 19:42
  • 1
    Your `RecipeDetail` component is already getting the current recipe slug via `useParams`. I would `useSelector` to select the details of that particular recipe, if they exist in the store `useSelector((store) => store.recipes[slug])`. I would make your `useEffect` conditional such that we only need to fetch if the recipe is `undefined`. While the recipe is `undefined`, you can render a loading screen. – Linda Paiste Jan 26 '21 at 19:44
  • Would it be helpful if I put that into an answer? It's understandable if you just want a quick fix. – Linda Paiste Jan 26 '21 at 19:49
  • @LindaPaiste Thanks for your reply. What is the benefit on doing it this way? Since a user can only see one recipe at a time, whats the benefit on storing all of the recipes that the user has visited in the Redux Store? – Alvaro Bataller Jan 27 '21 at 02:01
  • The benefit would be to reduce duplicated API calls. Like if you load a list of 10 recipes and then the user clicks on one, if the data that you got from the list is the same (ie. a complete object and not a summary) then you don’t need to hit the API again. – Linda Paiste Jan 27 '21 at 02:23

0 Answers0