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

Tracked mirror approach for prop notifications #591

Open
wants to merge 18 commits into
base: master
Choose a base branch
from

Conversation

betocantu93
Copy link
Contributor

@betocantu93 betocantu93 commented May 10, 2021

@snewcomer This is another approach for #586, property notifications

We use tracked-built-ins to have a common tracked object reference that we can keep notifying of changes by lazily consuming tags on every changeset.get.

This PR also implements addObserver and removeObserver to allow client code to add / remove observers in an ergonomic way to a changeset.

The only "downside" AFAIK is that for classic computeds, we have to manually subscribe to the changeset.mirror property. One way to solve this, is to just use native getters or, create a native getter which consumes the value and then add that native getter to the dependencies of your computed, i.e.

get myProp() {
  return this.args.changeset.get(this.args.path);
}

@computed('myProp')
get someComputed() {
 return ....
}

//or
@computed(`args.changeset.mirror.yourpath`)
...

Here's the sandbox im using for this DEMO:
https://github.com/betocantu93/changeset-tests/tree/try-mirror

Notice: please use try-mirror branch

@betocantu93 betocantu93 changed the title Try mirror Tracked mirror approach for prop notifications May 10, 2021
Copy link
Collaborator

@snewcomer snewcomer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am liking the ideas we are exploring here!

import ArrayProxy from '@ember/array/proxy';
import ObjectProxy from '@ember/object/proxy';
import { notifyPropertyChange } from '@ember/object';
import mergeDeep from './utils/merge-deep';
import isObject from './utils/is-object';
import { tracked } from '@glimmer/tracking';
import { tracked } from 'tracked-built-ins';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we would want both? Or is the tracked-built-ins intended for things like @tracked _changes; as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 not sure, TBH! Gonna have a look

}

deepAddObserver(this[VIRTUAL_MIRROR], key, callback);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So is changest.addObserver a common pattern that we should support and/or encourage?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Manually adding observers in general is not that common, I needed them for a very specific use case that I'm not entirely sure if it could be solved by native getters and this mirror approach, still having a way to add them gives more flexibility and an actual way to do it, which doesn't exist today, also... adding them to the content have it's own set of inconveniences

get(key) {
safeGet(this[VIRTUAL_MIRROR], key); //consume tag for native tracked dependencies
return super.get(...arguments);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this handle the obj.key case as well? Or do we have the ability to handle this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think yes? Because of the Proxy traps all access 🤔

@dependentKeyCompat
get mirror() {
return this[VIRTUAL_MIRROR];
}
Copy link
Collaborator

@snewcomer snewcomer May 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this meant to be accessed anywhere? I don't see this.mirror anywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mainly for public access, for the classic computeds case

@@ -188,6 +280,26 @@ export class EmberChangeset extends BufferedChangeset {

return this;
}

get(key) {
safeGet(this[VIRTUAL_MIRROR], key); //consume tag for native tracked dependencies
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this meant to be safeGet(this.mirror, key)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed the pattern of using this[KEYWORD] on internal usages, the get mirror(){} is meant to be used by client code, public access, like.... get data(){}

}
}
const pathToNotify = maybeDynamicPathToNotify ? maybeDynamicPathToNotify : lastPath;
notifyPropertyChange(current, pathToNotify);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should make notifyPropertyChange opt-in in a next major 4.0 release? Generally, we gave patterns that obfuscate the need for these reactivity systems.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or am I missing a crucial need for notifyPropertyChange, even in the new reactivity world?

Copy link
Contributor Author

@betocantu93 betocantu93 May 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔🤔🤔 maybe it's no longer needed, you're right, gonna have a look, one thing I still need to look for is reactivity around arrays, i.e. computeds which use changeset.someArray.@each.value

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants