1

I'm trying to set the value of state.case.summaries.get(action.summary.id).metadata.summaryRank with a reducer. summaries is Immutable.Map<string, Summary>;

I've tried this:

        return {
            ...state,
            'case' : {
                ...state.case,
                summaries : {
                    ...state.case.summaries,
                    [action.summary.id] : {
                        ...state.case.summaries.get(action.summary.id),
                        metadata : {
                            ...state.case.summaries.get(action.summary.id).metadata,
                            summaryRank : action.newRank
                        }
                    }
                }
            }
        };

but I'm losing my types:

`state` before:
ApplicationState {_map: Map, __ownerID: undefined}
after:
{_map: Map, __ownerID: undefined, case: {…}}


`case` before:
Case {_map: Map, __ownerID: undefined}
after:
{_map: Map, __ownerID: undefined, summaries: {…}}


`summaries` before
Map {size: 44, _root: HashArrayMapNode, __ownerID: undefined, __hash: undefined, __altered: false}
after
{size: 44, _root: HashArrayMapNode, __ownerID: undefined, __hash: undefined, __altered: false, …}

so then if I later try to do summaries.get(summaryId), I end up with applicationState.case.summaries.get is not a function


Then I've tried doing this way:

        return state.set('case',
            state.case.set('summaries',
                state.case.summaries.get(action.summary.id).set(//not sure what to do here
                    state.case.summaries.get(action.summary.id).set('metadata',
                        state.case.summaries.get(action.summary.id).metadata.set('summaryRank',
                            action.newRank
                        )
                    )
                )
            )
        );

but I'm getting:

 [exec]
 [exec]  46                     state.case.summaries.get(action.summary.id).set(
 [exec]                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 [exec]  47                         state.case.summaries.get(action.summary.id).set('metadata',
 [exec]     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 [exec] ...
 [exec]  51                         )
 [exec]     ~~~~~~~~~~~~~~~~~~~~~~~~~
 [exec]  52                     )
 [exec]     ~~~~~~~~~~~~~~~~~~~~~
 [exec]
 [exec] .../services/store/caseReducer.ts(46,21): error TS2346: Supplied parameters do not match any signature of call target.
 [exec]

More Info


case.ts

const CaseRecord = Immutable.Record({
    id: '',
    reserved:false,
    loaded: false,
    summaries: Immutable.Map<string, Summary>(),
    caseMetadata: new models.CaseMetadata(),
});

export class Case extends CaseRecord {
    public id: string;
    public loaded: boolean;
    public reserved: boolean;
    public summaries: Immutable.Map<string, Summary>;
    public caseMetadata: models.CaseMetadata;

    constructor(properties?: any) {
        super(properties);
    }

    public asMutable(): Case {
        let record = <Case>super.asMutable();
        record.summaries = this.summaries.asMutable();

        this.summaries.forEach((summary) => {
            record.summaries = record.summaries.set(summary.id, <models.Summary>summary.asMutable());
        });
        record.caseMetadata = this.caseMetadata.asMutable();

        return record;
    }
}

applicationState.ts

const ApplicationStateRecord = Immutable.Record({
    case: new models.Case(),
    lookupValues: {}
});

export class ApplicationState extends ApplicationStateRecord {
    public case: models.Case;
    public lookupValues: LookupValues;

    constructor(properties?: any) {
        super(properties);
    }
}
Smern
  • 18,746
  • 21
  • 72
  • 90
  • It's a little hard to figure out what's going on because I don't see the ApplicationState and Case classes. Is there any reason why you make them classes and not use the Immutable.js Maps the whole way through? – Kevin Raoofi Apr 27 '18 at 16:03
  • @KevinRaoofi - I've added those classes above. – Smern Apr 27 '18 at 16:13
  • Possible duplicate of [How can I set a deeply nested value in Immutable.js?](https://stackoverflow.com/questions/32350575/how-can-i-set-a-deeply-nested-value-in-immutable-js) – Simon Baumgardt-Wellander Apr 28 '18 at 15:04

1 Answers1

2

Be careful using class types in your redux store -- you have to make sure that you're not using normal JS objects.

When you do something like:

return { /* stuff */ }

There's no way for redux to be able to tell that you're deriving it off another type. You can apply the original prototype doing something like:

return Object.assign(new ApplicationState(), { /* stuff */ })

However, it's pretty tedious when there's a lot of nesting.

Thankfully, it seems like you're using Immutable stuff all the way through, which makes it fairly easy to update nested types. I believe you can just do:

return state.setIn(
    ['case', 'summaries', action.summary.id, 'metadata', 'summaryRank'],
    action.newRank
)

This will create keys as needed, as well, using Immutable's Maps as needed.

Kevin Raoofi
  • 1,023
  • 11
  • 16