reduxjs / redux

A JS library for predictable global state management
https://redux.js.org
MIT License
60.94k stars 15.26k forks source link

Handling complex store relationships #386

Closed jackcallister closed 9 years ago

jackcallister commented 9 years ago

This is more database related than just Redux but here goes...

Let's say Redux has a store that looks like this:

{
  meals: {
    collection: [{
      name: 'Turkey',
      id: 1
    },{
      name: 'Fish',
      id: 2
    }],

    selectedMeals: [1]
  }
}

My interface has the ability to delete a meal, here's the relevant reducer function:

function deleteMeal(state, action) {
  const meals = state.collection.filter((meal) => {
    return meal.id !== action.payload
  })

  return {
    ...state,
    collection: meals,
  }
}

There is a bug here. I must also remove the id from the selectedMeal array too.

function deleteMeal(state, action) {
  const meals = state.collection.filter((meal) => {
    return meal.id !== action.payload
  })

  const selectedMealIds = state.selectedMealIds.filter((id) => {
    return id !== action.payload
  })

  return {
    collection: meals,
    selectedMealIds
  }
}

The interesting thing is my interface doesn't bork but I know the store is not 'truthful'. I must manually remove the id reference from the store. That makes sense but I am wondering if there is a better way?

Taking a look at this Baobab article provides some interesting thoughts.

gaearon commented 9 years ago

Deleting is always tricky because there is no first class notion of a schema. We don't know which parts of state are actually references to IDs and need to be updated.

I see three solutions:

  1. For entities which are rarely deleted, reload the page. It's not too bad if it happens rarely (e.g. selecting something like a Facebook group or a blog post leading to refresh is normal IMO, you don't do that every day).
  2. For entities that may often get deleted, you may consider a status field. For example, deleting may set isDeleted on the model. This way it's still there, but it's up to the UI to filter deleted items out.
  3. You can make schema a first class concept, just like it's first class in normalizr. If you have a schema, you can write a reducer factory that creates reducers adhering to this schema. These reducers will know when to remove IDs from foreign key fields because they know what the schema looks like.
jackcallister commented 9 years ago

Thanks for the advice. I think the isDeleted field is a pragmatic solution suitable for my scenario. I'm not very familiar with normalizr but the concept looks appealing, especially for a larger more relationship heavy data set. I'll have to take a closer look.