How to normalize data by different schemas based on the entity parent's value with normalizr?

1.3k Views Asked by At

I'd like to normalize data in this format:

    [
      {
        id: 3,
        status: "active",
        created: "2017-07-03T15:36:11+02:00",
        published: "2017-07-03T15:38:07+02:00",
        linkables: [
          {
            data: {
              content: "Text content of the linkable",
              id: 200
            }
            linkable_type: "content",
            module_id: 3,
            module_type: "case_study"
          },
          {
            data: {
              content: "https://foobar.wee/url_of_the_media.png",
              id: 205
            }
            linkable_type: "media",
            module_id: 3,
            module_type: "case_study"
          },
        ]
      },
      ...
    ]

into something like this:

    {
      entities: {
        contents: {
          200: {
            content: "Text content of the linkable",
            id: 200
          }
        },
        media: {
          205: {
            content: "https://foobar.wee/url_of_the_media.png",
            id: 205
          }
        },
        linkables: {
          3_case_study_content_200: {
            data: 200, // in the contents key
            linkable_type: "content",
            module_id: 3,
            module_type: "case_study"
          },
          3_case_study_media_205: {
            data: 205, // in the media key
            linkable_type: "media",
            module_id: 3,
            module_type: "case_study"
          }
        },
        caseStudies: {
          3: {
              id: 3,
              status: "active",
              created: "2017-07-03T15:36:11+02:00",
              linkables: ["3_case_study_content_200", "3_case_study_media_205"]
          }
        }
      },
      result: [3]
    }

Using the 2.x version of the normalizr makes this easy thanks to this PR https://github.com/paularmstrong/normalizr/pull/132 that adds a function one can use to dynamically decide what scheme to use when normalizing by the value in the parent object.

However, in the version 3.x.x this additional function did not make it. According to the docs, I'd say that schemaAttribute does the job.

Having this piece of code unfortunately does not give me the wanted result...

    export const caseStudies = new schema.Entity('caseStudies');

    export const media = new schema.Entity('media',);
    export const content = new schema.Entity('contents');

    export const linkablesSchema = new schema.Entity(
      'linkables',
      {},
      {
        idAttribute: (itm) => {
          const id = itm.data.id;
          return `${itm.module_id}_${itm.module_type}_${itm.linkable_type}_${id}`;
        }
      }
    );

    export const linkableSchemaMap = {
      content,
      media
    };

    caseStudies.define({
      linkables: [linkablesSchema]
    });


    export const lschema = new schema.Array(linkableSchemaMap, (value, parent) => {
      return parent.linkable_type; // this would replace the PR mentioned above?
    });

    linkablesSchema.define({
      data: lschema
    });

Thank you for your help!

1

There are 1 best solutions below

9
On

The most straightforward way that came to mind is to use processStrategy inside of the linkablesSchema options to apply linkable_type value as a property:

export const caseStudies = new schema.Entity("caseStudies");
export const mediaSchema = new schema.Entity("media");
export const contentsSchema = new schema.Entity("contents");
export const linkablesSchema = new schema.Entity("linkables", {}, {
  idAttribute: itm => `${itm.module_id}_${itm.module_type}_${itm.linkable_type}_${itm.data.id}`,
  processStrategy: (value) => ({ 
    ...value,
    data: value.data.id,
    [value.linkable_type]: value.data,
  })
});

linkablesSchema.define({
  content: contentsSchema,
  media: mediaSchema
})

caseStudies.define({
  linkables: [linkablesSchema]
});

normalize(data, [caseStudies]);

And here's the sandbox. Hope it helps.