0

So I'm using ngrx for managing the state in my application. I tried to add a new property (selected shifts) which should look like this:

state: {
    shifts: {
        selectedShifts: [
            [employeeId]: [
                [shiftId]: shift
            ]
        ]
    }
}

at the moment, my state looks like this:

state: {
    selectedShifts: {
        [employeeId]: {
            [shiftId]: shift
        }
    }
}

so as you can see, my "selected shift" is a property, not an array - which makes it diffictult to add/remove/query the state.

How do I compose the state to look like I want it?

This is what I tried in the reducer:

return {
    ...state,
    selectedShifts: {
      ...state.selectedShifts,
      [action.payload.employeeId]: {
        ...state.selectedShifts[action.payload.employeeId],
        [action.payload.shiftId]: action.payload[shift.shiftId]
      }
    }
  };

Now when I try to return the state in the way I'd like to, this is the result:

state: {
    selectedShifts: {
        [action.payload.employeeId]: 
            [0]: {[action.payload.shiftId]: { shift }}
    }
}

What am I missing here? When I try to replace the {} items which should be [] this error comes up: "," expected.

Oh yea, I would like the index of the array to be the id of the specific shift and not [0], [1]...

Is this possible at all? Would it be a bad idea to change the index from numerics to the actual shift's id?

rawk
  • 508
  • 1
  • 7
  • 13

1 Answers1

0

Array length kind of miss behaves when you add data at numeric index points. This might get you into problems with array methods using length join, slice, indexOf etc. & array methods altering length push, splice, etc.

var fruits = [];
fruits.push('banana', 'apple', 'peach');

console.log(fruits.length); // 3

When setting a property on a JavaScript array when the property is a valid array index and that index is outside the current bounds of the array, the engine will update the array's length property accordingly:

fruits[5] = 'mango';
console.log(fruits[5]); // 'mango'
console.log(Object.keys(fruits));  // ['0', '1', '2', '5']
console.log(fruits.length); // 6

There is no problem selecting / updating state from object, it's just a bit different from what you're probably used to. With straight hashmap { objectId: Object } finding the required object to update / remove is the fastest possible if changes are defined for object id.

I know your problem is related to NGRX but reading Redux immutable patterns is going to definitely help you out here for add / update / remove objects from the state. https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns

Generally you don't want to have arrays in state ( at least large arrays ) object hashmaps are a lot better.

To get array of your selected user shifts for views you could do something like. Note this is not a shift indexed array just array of shifts under userId property. From original state form following state.

state: {
    selectedShifts: {
        [employeeId]: {
            [shiftId]: shift
        }
    }
}
const getSelectedShiftsAsArray = this.store.select( getSelectedShifts() )
.map(
    userShifts => {
        // get array of object ids
        const userIds = Object.keys( userShifts );

        const ret = {};

        for( const userId of userIds ) { 
            const collectedShifts = [];
            // convert Dictionary<Shift> into a Shift[]
            // get array of shift ids
            const shiftIds = Object.keys( userShifts[userId] );
            // map array of shift ids into shift object array
            collectedShifts = shiftIds.map( shiftId => userShifts[shiftId] );
            // return value for a userId
            ret[userId] = collectedShifts;

        }

        return ret;
});

Code is completely untested and just for a reference one level up from pseudocode. You could easily convert that into a NGRX selector though. The state is there just for the storage, how you model it for use in components is upto selector functions & components themselves.

If you really really need it you could add.

ret[userId].shiftIds = shiftIds;
ret[userId].shifts = collectedShifts;

But it really depends on how you plan to use these.

From my personal experience I would separate shift entities from selectedShifts but how you organise your state is completely up to you.

state: {
    shifts: {
        // contains shift entities as object property map id: entity
        entities: Dictionary<Shift>,
        selectedShifts: [
            [employeeId]: number[] // contains ids for shifts
        ]
    }
}

Now updating / removing and adding a shift would just be setting updated data into path shifts.entities[entityId]

Also selectedShifts for employeeId would be about checking if id is already in there and appending it into an array if it wasn't. ( If these arrays are humongous I'd go with object hash here too for fast access. <employeeId>: {shiftId:shiftId} ).

Check also: redux: state as array of objects vs object keyed by id

MTJ
  • 1,059
  • 1
  • 10
  • 23