Search code examples
reactjsreduxnormalizr

React Redux - Normalizr - Selecting from nested data - select by id


So kind of stuck on this problem. I've gotten a response for a list of artwork, which is a deeply nested response. I have normalized this response using the normalizr library.

You can check out the Schema below:

../schema.js

import { Schema, arrayOf } from 'normalizr'

const artboardSchema = new Schema('artboards', { idAttribute: 'id' });
const artistSchema = new Schema('artists', { idAttribute: 'id' });
const lastArtSchema = new Schema('lastArt', { idAttribute: 'id' });
const artSchema = new Schema('art', { idAttribute: 'id' });
const artAssetSet = new Schema('artAssetSet', { idAttribute: 'url'});

artboardSchema.define({
    collaborated_by: arrayOf(artistSchema),
    last_art: lastArtSchema,
    art: arrayOf(artSchema)
});

artSchema.define({
   artasset_set: arrayOf(artAssetSet),
    performed_by: artistSchema
});

lastArtSchema.define({
   performed_by: artistSchema
});

export { artboardSchema }

This is working great so far, the data is being normalized and I'm getting the following response saved in the state tree:

artboard:
    entities:
        artboards {}
        artists {}
        lastArt {}
        art {}
        artAssetSet {}
    result:
        [2,3,4,5,6]

So now I have a component that gets this data and displays it.

../ArtCard.jsx

const ArtCard = (props) => {
    return (
        <div className={props.cardContainer}>
            {props.item.result.map( id =>
                <div className={props.itemClass} value={id} key={id}>
                    <h5>{props.item.entities.artboards[id].name}</h5>

                    <p>{props.item.entities.artboards[id].uid}</p>

                    {props.item.entities.artboards[id].collaborated_by.map( id =>
                    <Avatar key={id} name={props.item.entities.artists[id].name} size={40} round={true}/>
                    )}
                    <button className="btn btn-primary" style={{cursor: 'pointer'}} onClick={() => {
                        props.onSubmit()
                    }
                    }>Details</button>
                </div>
            )}
        </div>
    );
};

The above component gets the entire artboard object from the state tree, and then we run .map on it, to get the artboards by id.

The trouble I'm having is with regards to last_art, I want to display the artist that did the last art, I'm able to get the id by placing {props.item.entities.artboards[id].last_art}, but I'm having trouble using this to get the info from lastArt, i.e. this would be something like {props.item.entities.lastArt[id].date} or {props.item.entities.lastArt[id].artist} as last_art is not an array, but just a Number, meaning I can't use map on it.

Is there a better way to do this? What am I missing in this case?


Solution

  • So I was able to solve this, it was actually a fairly simple solution. I had to account for the last_art id being null as well.

    const PatientCard = (props) => {
            return (
                <div className={props.cardContainer}>
                    {props.item.result.map( id =>
                        <div className={props.itemClass} value={id} key={id}>
                            <h5>{props.item.entities.artboards[id].name}</h5>
    
                            <span className={props.lastArtClass}>
                                {props.item.entities.artboards[id].last_art !== null &&
                                <span>
                                    <span>{props.item.entities.artists[props.item.entities.lastArt[props.item.entities.artboards[id].last_art].performed_by].name}, </span>
                                    <FormattedRelative value={props.item.entities.lastArt[props.item.entities.artboards[id].last_art].date} />
                                </span>
                                }
                                {props.item.entities.artboards[id].last_art === null &&
                                <p>N/A</p>
                                }
                            </span>
    
                            {props.item.entities.artboards[id].collaborated_by.map( id =>
                                <Avatar key={id} name={props.item.entities.artists[id].name} size={40} round={true}/>
                            )}
                            <button className="btn btn-primary" style={{cursor: 'pointer'}} onClick={() => {
                                props.onSubmit()
                            }
                            }>Details</button>
                        </div>
                    )}
                </div>
            );
        };
    

    I think there is definitely a better way to do this using Reselect.