Skip to content
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

Patch generated when setting equivalent valueType models #463

Open
kcoop opened this issue Jun 11, 2022 · 8 comments
Open

Patch generated when setting equivalent valueType models #463

kcoop opened this issue Jun 11, 2022 · 8 comments
Assignees
Labels
🐛 bug Something isn't working 🤞 maybe fixed? Maybe it is fixed 📑 merged to master Feature done and merged to master 🎈 released A fix that should close the issue has been released

Comments

@kcoop
Copy link
Contributor

kcoop commented Jun 11, 2022

It seems like models with valueType: true are meant to be thought of like structs, and as such one would expect setting a valueTyped prop to a new model with the same prop values not to generate a patch, since the value has not really changed. But the following test fails:

import { model, Model, Patch, patchRecorder, prop } from 'mobx-keystone';

@model('app/CurrencyAmount')
class CurrencyAmount extends Model(
  {
    amount: prop<number>(),
    currency: prop<string>('USD')
  },
  { valueType: true }
) {}

@model('/app/TestContainer')
class TestContainer extends Model({
  balance: prop<CurrencyAmount>(() => new CurrencyAmount({ amount: 0, currency: 'USD' })).withSetter()
}) {}

describe('Patch generation', () => {
  it('should not emit a patch when assigning a valueType with equal values', () => {
    const container = new TestContainer({});
    let patchOccurred = false;
    const recorder = patchRecorder(container, {
      onPatches: (newPatches: Patch[], inversePatches: Patch[]) => {
        patchOccurred = true;
      }
    });

    container.setBalance(new CurrencyAmount({ amount: 0, currency: 'USD' }));
    expect(patchOccurred).toBe(false);
    recorder.dispose();
  });
});

This is problematic for diffing two snapshots - the diff shows erroneous non changes.

Looking at the stack, mobx-keystone generates a patch without comparing the props of the models. Wondering if there should be a special case test in the 'update' case of objectDidChange for models that have valueType: true?

@xaviergonz
Copy link
Owner

Is this about the patches being generated by applySnapshot over another node being "do nothing" patches for value types?

@kcoop
Copy link
Contributor Author

kcoop commented Jun 12, 2022

Yes, exactly, that's the issue. They are do nothing patches, which would be confusing to users when presented as part of a diff. I can of course filter them out externally if necessary, but it seems like an upstream fix might be more suitable.

@kcoop
Copy link
Contributor Author

kcoop commented Jun 12, 2022

(They also add a little weight to the 'versioned models' in this app, where versions are implemented as a collection of patches with a base model)

@xaviergonz
Copy link
Owner

xaviergonz commented Jun 12, 2022

I can make applySnapshot over valueTypes not generate patches if nothing changed, but your unit test would still fail (since mobx-keystone assumes a value changed when you are assigning a whole new instance created via "new" to avoid expensive deep checking). Would that solve your issue?

@kcoop
Copy link
Contributor Author

kcoop commented Jun 12, 2022

That would be great! But I don't understand how that test (which was really only for debugging purposes) would then fail - wouldn't no patch be generated?

@xaviergonz
Copy link
Owner

A patch is always generating when doing this:

prop x = new Model(...)

no matter its props

What I'll change is how value types behave. Right now they always generate a clone (new X...) before being assigned to a prop. What I'll change is that the clone will only be generated if the model has a previous parent. If it has no parent (such as the case when using applySnapshot) it won't use clone, therefore it won't internally use "new X..." and not assume it is a whole new model instance, so that it can be properly reconciled.

@xaviergonz
Copy link
Owner

Should be now fixed in v0.69.1 (the applySnapshot case as discussed above)

@xaviergonz xaviergonz self-assigned this Jun 12, 2022
@xaviergonz xaviergonz added 🐛 bug Something isn't working 🎈 released A fix that should close the issue has been released 🤞 maybe fixed? Maybe it is fixed 📑 merged to master Feature done and merged to master labels Jun 12, 2022
@kcoop
Copy link
Contributor Author

kcoop commented Jun 12, 2022

Thanks, confirmed, that does fix my issue. Appreciate the quick turnaround!

Not critical, but when thinking of valueTypes like structs, it will still be slightly confusing if a patch is generated by assigning a new equivalent value (e.g. setting a point with the same x,y, or a new color with the same RGB value).

I understand the perf concern though, and maybe there's some other semantic integrity issue I'm missing, but I wonder if an identity comparison of the props would be worth it? (of course only for valueTypes)

Or maybe it's wrong to think of these like structs...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛 bug Something isn't working 🤞 maybe fixed? Maybe it is fixed 📑 merged to master Feature done and merged to master 🎈 released A fix that should close the issue has been released
Projects
None yet
Development

No branches or pull requests

2 participants