New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Best way to update related state fields with split reducers? #946

Closed
gdborton opened this Issue Oct 24, 2015 · 4 comments

Comments

2 participants
@gdborton

I'm trying to work out the ideal way to update several top level fields on my state tree while still maintaining split reducers.

Here's a simple solution that I've come up with.

var state = {
  fileOrder: [0],
  files: {
    0:{
      id: 0,
      name: 'asdf'
    }
  }
};

function handleAddFile(state, action) {
  return {...state, ...{[action.id]:{id: action.id, name: action.name}}};
};

function addFileOrder(state, action) {
  return [...state, action.id];
}

// Adding a file should create a new file, and add its id to the fileOrder array.
function addFile(state, action) {
  let id = Math.max.apply(this, Object.keys(state.files)) + 1;
  return {
    ...state,
    fileOrder: addFileOrder(state.fileOrder, {id}),
    files: handleAddFile(state.files, {id, name: action.name})
  };
}

Currently I'm able to dispatch a single action {type: ADD_FILE, fileName: 'x'}, then addFile creates an action internally to send to addFileOrder and addFile.

I'm curious if it is considered a better approach to do either of the below.

  1. Instead dispatch two actions, one to add a file, then get it's id and dispatch an ADD_TO_FILE_ORDER action with the id.
  2. OR Fire and action like {type: ADD_FILE, name: 'x', id: 1}, instead of allowing addFile to calculate the new id. This would allow me to use combineReducers and filter on action type.

This example is probably trivial, but my actual state tree is a bit more complicated, with each file being added also needing to be added to other entities.

@gdborton

This comment has been minimized.

Show comment
Hide comment
@gdborton

gdborton Oct 24, 2015

For some additional context, a more complete state tree would look like this.

{
    "fileOrder": [0]
    "entities": {
        "files": {
            0: {
                id: 0,
                name: 'hand.png'
            }
        },
        "animations": {
            0: {
                id: 0,
                name: "Base",
                frames: [0]
            }
        },
        "frames": {
            0: {
                id: 0,
                duration: 500,
                fileFrames: [0]
            }
        },
        "fileFrames": {
            0: {
                id: 0,
                file: 0,
                top: 0,
                left: 0,
                visible: true
            }           
        }
    }
}

Adding a file would need to:

  1. Add it to the files hash.
  2. Add it to the fileOrder array.
  3. Add a fileFrame referencing the file, for each of the frames.
  4. Add each new fileFrame to the frame that it was created for.

The last two points make me wonder if I'd be able to use combineReducers at all.

For some additional context, a more complete state tree would look like this.

{
    "fileOrder": [0]
    "entities": {
        "files": {
            0: {
                id: 0,
                name: 'hand.png'
            }
        },
        "animations": {
            0: {
                id: 0,
                name: "Base",
                frames: [0]
            }
        },
        "frames": {
            0: {
                id: 0,
                duration: 500,
                fileFrames: [0]
            }
        },
        "fileFrames": {
            0: {
                id: 0,
                file: 0,
                top: 0,
                left: 0,
                visible: true
            }           
        }
    }
}

Adding a file would need to:

  1. Add it to the files hash.
  2. Add it to the fileOrder array.
  3. Add a fileFrame referencing the file, for each of the frames.
  4. Add each new fileFrame to the frame that it was created for.

The last two points make me wonder if I'd be able to use combineReducers at all.

@gaearon gaearon added the question label Oct 24, 2015

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Oct 24, 2015

Collaborator

Not sure if it helps you but what we usually suggest is to have a very specific structure to the actions that involve entities. It's nice when they mirror the entities state field. For example:

{
  type: 'ADD_FILE',
  files: [0],
  entities: {
    files: {
      0: { name: 'something.jpg', animations: [0] }
    },
    animations: {
     0: { ... }
    }
  }
}

When the entities inside the action look exactly like entities in state, you can use a helper like _.merge from Lodash to write an entities reducer:

import merge from 'lodash/object/merge';

// Updates an entity cache in response to any action with response.entities.
function entities(state = { users: {}, repos: {} }, action) {
  if (action.response && action.response.entities) {
    return merge({}, state, action.response.entities);
  }

  return state;
}
Collaborator

gaearon commented Oct 24, 2015

Not sure if it helps you but what we usually suggest is to have a very specific structure to the actions that involve entities. It's nice when they mirror the entities state field. For example:

{
  type: 'ADD_FILE',
  files: [0],
  entities: {
    files: {
      0: { name: 'something.jpg', animations: [0] }
    },
    animations: {
     0: { ... }
    }
  }
}

When the entities inside the action look exactly like entities in state, you can use a helper like _.merge from Lodash to write an entities reducer:

import merge from 'lodash/object/merge';

// Updates an entity cache in response to any action with response.entities.
function entities(state = { users: {}, repos: {} }, action) {
  if (action.response && action.response.entities) {
    return merge({}, state, action.response.entities);
  }

  return state;
}
@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Oct 24, 2015

Collaborator

I think, to help you further, we'd need a runnable project because I don't feel there's enough information about the kind of actions you're going to have, and how the state changes.

Since this is a question, I encourage you to post it to StackOverflow instead because lately we've been trying to keep usage questions out of the repository. Feel free to link to it here.

Collaborator

gaearon commented Oct 24, 2015

I think, to help you further, we'd need a runnable project because I don't feel there's enough information about the kind of actions you're going to have, and how the state changes.

Since this is a question, I encourage you to post it to StackOverflow instead because lately we've been trying to keep usage questions out of the repository. Feel free to link to it here.

@gaearon gaearon closed this Oct 24, 2015

@gdborton

This comment has been minimized.

Show comment
Hide comment
@gdborton

gdborton Oct 24, 2015

@gaearon No problem, reposted on SO. -http://stackoverflow.com/questions/33321397/best-way-to-update-related-state-fields-with-split-reducers

For a bit of background, I'm building a sprite editor (not in a great state) - https://github.com/gdborton/petulant-ninja

Each each file added needs to be added to every frame for every animation, and put in the fileOrder field. fileOrder is the relative z-index when rendering the file for any given frame.

@gaearon No problem, reposted on SO. -http://stackoverflow.com/questions/33321397/best-way-to-update-related-state-fields-with-split-reducers

For a bit of background, I'm building a sprite editor (not in a great state) - https://github.com/gdborton/petulant-ninja

Each each file added needs to be added to every frame for every animation, and put in the fileOrder field. fileOrder is the relative z-index when rendering the file for any given frame.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment