5

I'm trying to figure out a good structure to create a many to many relationship in Redux. For this example we have images (which can have multiple tags) and tags (which can have multiple images).

Is this a good way to structure it in Redux?

images: {
    1: {
        title: "A bunch of hills"
    },
    2: {
        title: "Hiking in a forest"
    }
},
tags: {
    1: {
        title: "landscape"
    },
    2: {
        title: "outdoors"
    }
},
imagesTags: {
    [
        image: 1,
        tag: 1
    ],
    [
        image: 1,
        tag: 2
    ],
    [
        image: 2,
        tag: 2
    ]
}

It does mean I have to create a separate reducer which messes with my modular structure but I guess that is always going to be the result of having related reducers.

joshhunt
  • 4,685
  • 2
  • 31
  • 56
  • I don't want to assume that you don't need to do this, but in my experience, this type of relationship is for the database. You send your images down with their associated tags. You rely on the API to be the source of truth and allow your client to be fairly dumb. What's the use case that requires this type of cross lookup on the client? – Samo Feb 15 '17 at 05:58
  • Fair point, however in my case I don't have a database. It's a small personal project and everything is stored inside the browsers local storage. – joshhunt Feb 15 '17 at 08:40
  • 2
    Even if you did have a database wouldn't you want some sort of relationship to make sure they stay in sync? For example if you send the images with their tags what happens if you want to load a "tags" page? Send another request to the server to get tags and their associated images? Then what happens if you remove an image from a tag? Would you have to remove it from both the images and the tags reducers? – joshhunt Feb 15 '17 at 08:42
  • you can do things like that to an extent, sure. After removing the image you could find tags that have that image and update them. I think you reach a point, though, where this becomes impractical. I'm partial to setting a `stale` or `expired` flag on my lists when a data change is more complex than what I want to handle on the front end. The component detects the `stale` state and reloads from the server. – Samo Feb 15 '17 at 16:29

1 Answers1

5

Yep, this is an example of a "normalized" state. The Redux docs cover normalization in the Structuring Reducers section. See Structuring Reducers - Prerequisite Concepts, Structuring Reducers - Normalizing State Shape, and Structuring Reducers - Updating Normalized Data. Also, the Redux FAQ discusses normalization in "How do I organize nested or duplicate data in my state?".

Beyond that, I'm a big fan of using a library called Redux-ORM to manage that relational data in my Redux store. I've written a couple posts that discuss how I use it: Practical Redux Part 1: Redux-ORM Basics and Practical Redux Part 2: Redux-ORM Concepts and Techniques. The rest of my "Practical Redux" tutorial series also shows this library in action.

Hugo
  • 800
  • 11
  • 22
markerikson
  • 42,022
  • 7
  • 87
  • 109
  • 1
    Perfect thanks! This is an [exact example](http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html#relationships-and-tables) of what I was looking for. I tried searching for something like that but unfortunately it seems the docs are ranked quite low. Followup question, why does the join table use ids? And slightly off topic question, what is the purpose of allIds? I can't think of a example where it would be useful. – joshhunt Feb 15 '17 at 19:38
  • 5
    When I wrote the "Normalizing State Shape" doc page, I based the state shape examples off of the way Redux-ORM structures things. Redux-ORM defines those "through tables" for many-sided relationships, and since each entry is technically the value for a model instance, it needs its own ID. As for the ID arrays, while JS engines now have a fairly standardized process for iterating across keys in an object, you shouldn't rely on that to define ordering. Storing arrays of IDs allows you to define an order for items. Redux-ORM uses that for its "all items of type" lookups. – markerikson Feb 15 '17 at 19:46