Skip to content

Instantly share code, notes, and snippets.

@imranismail
Last active November 1, 2016 18:02
Show Gist options
  • Select an option

  • Save imranismail/0c462c744c31c9d3b3c43b4f76a59adb to your computer and use it in GitHub Desktop.

Select an option

Save imranismail/0c462c744c31c9d3b3c43b4f76a59adb to your computer and use it in GitHub Desktop.
How to normalize "belongs to" association redux?

Given this kind of relationship from

GET /projects/1

const response = {
  id: 1,
  name: 'Normalized Redux',
  type: 'project',
  task_lists: [
    {
      id: 1,
      name: 'Todo',
      type: 'task_list',
      project_id: 1,
      tasks: [
        {id: 1, type: 'task', name: 'As a user I can normalize belongs_to relation', task_list_id: 1},
        {id: 2, type: 'task', name: 'As a user I can register webhooks', task_list_id: 1},
        {id: 3, type: 'task', name: 'As a user I can listen for task completion event', task_list_id: 1}
      ],
      {
        id: 2,
        name: 'Doing',
        type: 'task_list',
        project_id: 1,
        tasks: []
      },
      {
        id: 1,
        name: 'Done',
        type: 'task_list',
        project_id: 1,
        tasks: []  
      }
    }
  ]
}

Given these schema definitions

const project = Schema('project')
const taskList = Schema('task_list')
const task = Schema('task')

task.define({
  task_list: taskList
})

taskList.define({
  tasks: arrayOf(task)
})

project.define({
  task_lists: arrayOf(taskList)
})

When normalized with normalize(response, project) I will get

{
  entities: {
    project: {
      1: {
        id: 1,
        name: 'Normalized Redux',
        type: 'project',
        task_lists: [1]
      }
    },
    task_list: {
      1: {
        id: 1
        name: 'Todo',
        type: 'task_list',
        project_id: 1,
        tasks: [1, 2, 3]
      },
      2: {
        id: 2,
        name: 'Doing',
        type: 'task_list',
        project_id: 1,
        tasks: []
      },
      3: {
        id: 3,
        name: 'Done',
        type: 'task_list',
        project_id: 1,
        tasks: []
      }
    },
    task: {
      1: {id: 1, type: 'task', name: 'As a user I can normalize belongs_to relation', task_list_id: 1},
      2: {id: 2, type: 'task', name: 'As a user I can register webhooks', task_list_id: 1},
      3: {id: 3, type: 'task', name: 'As a user I can listen for task completion event', task_list_id: 1}
    }
  },
  result: [1]
}

My reducers

const byIdReducer = (entity) => (state = {}, action) => {
  if (action.hasOwnProperty('normalized') &&
      action.normalized.entities.hasOwnProperty(entity)) {
    return merge({}, state, action.normalized.entities[entity])
  }

  return state
}

const idsReducer = (entity) => (state = [], action) => {
  if (action.hasOwnProperty('normalized') &&
      action.entity === entity) {
    return isArray(action.normalized.result)
      ? [...new Set([...state, ...action.normalized.result])]
      : [...new Set([...state, action.normalized.result])]
  }

  return state
}

If I make an API call to add a new task to a task list I get back a response like so

POST /task_lists/1/tasks

{
  id: 4,
  type: 'task',
  name: 'As an admin I can delete projects',
  task_list_id: 1
}

If I normalize that with normalize(response, task). I'll get this:

{
  entities: {
    task: {
      4: {
        id: 4,
        name: 'As an admin I can delete post',
        task_list_id: 1
      }
    }
  },
  result: [4]
}

This wont update the tasks references on entities.task_list[1].tasks and effectively makes the UI outdated

Taking note of the task_list_id reference

How do I propagate the changes entities.task_list[1].tasks as well?. I can't find an easy way that doesn't involve alot of boilerplate code for this with Redux and Normalizr.

Expected output:

{
  entities: {
    task: {
      4: {
        id: 4,
        name: 'As an admin I can delete post',
        task_list_id: 1
      }
    },
    task_list: {
      1: {
        tasks: [4]
      }
    }
  },
  result: [4]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment