Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Deferred object getters evaluation #569

Closed
wants to merge 15 commits into from

Conversation

zinoviev
Copy link
Contributor

PR for #390

@gaearon
Copy link
Contributor

gaearon commented Mar 2, 2017

Can I ask you to rebuild test/example/target.js so that it doesn't show up as a conflict?
Thanks!

@zinoviev
Copy link
Contributor Author

zinoviev commented Mar 2, 2017

@gaearon done

@gaearon
Copy link
Contributor

gaearon commented Mar 2, 2017

Great. I’m going to look at this and a few other PRs in a batch later next week.
Thank you so much for your work on this!

@zinoviev
Copy link
Contributor Author

zinoviev commented Mar 3, 2017

@gaearon
Sure. Where are couple of things you need to know

  1. I don't implement handling getters in TreeView yet. If general approach on evaluating and passing value between backend and frontend is fine it can be added in no time
  2. If you pass getter itself as a prop it won't be included in dehydrate check, so (...) rendering wont happens.
var obj = {
  name: 'Foo',
  get upper() {
   return this.name.toUpperCase();
 }
}

<Works prop={obj} />

<NotWorks prop={obj.upper} />

I believe this case can be handled too, it must be somewhere in agent/backend code, but I don't investigate this part yet.

On the other hand if user pass getter as prop do we really need to care about it's deferred eval, cause probably it's value will be used inside component logic immediately.

Copy link
Contributor

@jaredly jaredly left a comment

Choose a reason for hiding this comment

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

looks great!

}

render() {
return (<div style={style} onClick={this.handleClick.bind(this)}>(…)</div>);
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: don't need the parens

@jaredly
Copy link
Contributor

jaredly commented Mar 8, 2017

yeah I don't think we have to worry about the deferred eval of passing the getter in directly (and indeed I don't think we'd be able to)

@gaearon
Copy link
Contributor

gaearon commented Mar 8, 2017

I don't implement handling getters in TreeView yet. If general approach on evaluating and passing value between backend and frontend is fine it can be added in no time

Let’s do this then? It would help me if you also added some screenshots so I knew better how to test this.

@zinoviev
Copy link
Contributor Author

zinoviev commented Mar 8, 2017

@gaearon k, Will do it later this week

@zinoviev
Copy link
Contributor Author

zinoviev commented Mar 15, 2017

@gaearon @jaredly Updated PR, was not so trivial as I expect. Spend some time dealing with updates logic on Node component and store decorator.

To test:

  • Pass object with getter as prop
  • On details panel and in tree you should see (...)
  • Click on (...)
  • Getter value should be instead of (...) in details and tree

pr1

devtools-sample

var last = data.path.length - 1;
var propObj = data.path.slice(0, last).reduce((obj_, attr) => obj_ ? obj_[attr] : null, props);
propObj[data.path[last]] = data.value;
var newProps = assign({}, props);
Copy link
Contributor Author

@zinoviev zinoviev Mar 15, 2017

Choose a reason for hiding this comment

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

Props must be new object or bounded Node wont rerenders

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like tangential unrelated issue (worth raising). Why is this specific to getters (and not just any prop mutations)?

Copy link
Contributor Author

@zinoviev zinoviev Apr 20, 2017

Choose a reason for hiding this comment

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

Reason why - you don't mutate original props on requesting getter value. If you edit some prop(for example text) in devtools, it forwards changes to original page component. Original component props mutates and new set of props goes back to devtools store, immutable collection recreates, UI rerenders etc

But on requesting getter value we pass just singular value and do not make any changes to original prop. So this part of code needed to make frontend store's data in sync and refresh UI nicely.

But why do we need this line:
var newProps = assign({}, props);

Data stored in immutable map on store._nodes But node props is not immutable map, it just a plain JS object. In Node.js (component node on left panel) where is SCU check

shouldComponentUpdate(nextProps: PropsType) { return nextProps !== this.props; }

So if we just swap value inside this object UI wont refreshes correclty

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for explaining, this makes sense.

@gaearon
Copy link
Contributor

gaearon commented Mar 27, 2017

Just a heads up: I'm in the middle of some other work but I'll get back to this as soon as I can. Likely within one or two weeks.

@gaearon
Copy link
Contributor

gaearon commented Apr 20, 2017

How does this work with updates? Say I have a getter that is backed by an instance field. That field changes. Is there any way to re-evaluate the getter? Should there be?

@gaearon
Copy link
Contributor

gaearon commented Apr 20, 2017

I think ideally there should be a way to re-evaluate the getter so that you don’t end up with stale values forever. What do you think?

@gaearon
Copy link
Contributor

gaearon commented Apr 20, 2017

Let's take this example from #390 (comment):

var proto = {
  get upperName() { return this.name.toUpperCase()}
};

var instance = Object.create(proto);
instance.name = 'Foo';

When I am editing it, here is what I see:

Why does the getter disappear?

Copy link
Contributor

@gaearon gaearon left a comment

Choose a reason for hiding this comment

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

Let’s figure out a strategy for re-evaluating a getter, and also fix the issue I described in gif.

@gaearon
Copy link
Contributor

gaearon commented Apr 20, 2017

(Thanks for your work on this. I’m sorry I haven’t been very responsive lately. I think this is a good start but let’s ensure the feature works well in most cases so that people learn to trust it. Otherwise, even if we fix it in the future, people will avoid it because it did’t match their expectations before.)

Note I’ve pushed to your branch so you shouldn’t worry about merging with master.

@zinoviev
Copy link
Contributor Author

@gaearon I will check about hiding, must some caveat with object nesting

About re-evaluating: don't think we need worry at all due to unidirectional data flow nature and immutable state. If user change something what means we get new set of props and new eval buttons (...) Where is no way to for getter value to be staled - it's not evaluated yet or it's correct and actual. I will check it next as I back to code

@gaearon
Copy link
Contributor

gaearon commented Apr 20, 2017

Interesting. I tried a simple example where getter and setter write to a backing property, like

let obj = {
  get lol() {
    return this._lol;
  }
  set lol(val) {
    this._lol = val;
  }
}

and I don't think editing worked for me.

@zinoviev
Copy link
Contributor Author

@gaearon I take a look, seems updates on backend side affects getters not the way I expect. Will look further on this issues

@zinoviev
Copy link
Contributor Author

zinoviev commented May 4, 2017

@gaearon ping
Now editing props reset getters, so it can't be stalled.

Also your example with update object with getter defined on prototype should be fine

@gaearon
Copy link
Contributor

gaearon commented May 4, 2017

Cool! I’ll review when I get the time.

@zinoviev
Copy link
Contributor Author

@gaearon Merge master and resolve conflict, also I've added samples to prop tester mini-app

2017-05-31 14 33 53

@@ -165,4 +173,11 @@ function dehydrate(data: Object, cleaned: Array<Array<string>>, path?: Array<str
}
}

function isPropertyGetter(obj, prop) {
/* eslint-disable no-proto */
const descriptor = Object.getOwnPropertyDescriptor(obj.hasOwnProperty(prop) ? obj : obj.__proto__, prop);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this check one level deep? Could it be on __proto__.__proto__, __proto__.__proto__.__proto__, etc? Do we need a loop here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My fail, will fix asap!

@gaearon
Copy link
Contributor

gaearon commented Jun 2, 2017

When withSetter._value is edited, withGetter.upperName collapses even if it was previously shown. This is good because it could've been invalidated, but can be annoying if you're inside some deep object which you know doesn't change, and it collapses on every single edit to something else.

I wonder if maybe it's better to keep it around, but show a (...) after the value to trigger another evaluation? What do you think?

@gaearon
Copy link
Contributor

gaearon commented Jun 6, 2017

@zinoviev What are your thoughts on this? I wonder if we can look at how this compares to how Chrome displays getters.

@zinoviev
Copy link
Contributor Author

zinoviev commented Jun 6, 2017

@gaearon
The same way as we do actually - reset all getters on any edit, no extra buttons in UI. The only difference - they reset state only for the object you make changes, others in inspector does not updates.

I'm not sure it's actually intended, perhaps just the way devtools works.

Getter doesn't have to be pure functions:

var foo = {
  get wow() {
    return this._val.toUpperCase();
  },
  _val: 'fofofo',
};

var bar = {
  get bla() {
    return foo._val;
  }
}

So it's easy to make the outdated value in this case and if you want real value you must print object on console or add new watch.

As far I remember, in react devtools on any edit we get a new set of props every time user edit anything - immutable state and unidirectional flow for the rescue. So I think react devtools have a more reliable implementation for now.

I don't like refresh buttons, imo explicit is better than implicit and neither chrome has ones. We can try to implement chrome like behavior for single object updates, but thinks it's out of scope for this PR as I don't actually touch updates passing mechanism and it can be major changes, not sure if it worths.

@gaearon
Copy link
Contributor

gaearon commented Jun 6, 2017

I'm mostly concerned about this making edits inside large trees unusable if any parent is a getter. I understand that getter could mutate other objects but limiting this to the same object would make sure the feature doesn't break existing workflows for people. It's a compromise.

@zinoviev
Copy link
Contributor Author

zinoviev commented Jun 8, 2017

@gaearon Like this?
2017-06-08 02 04 20

@zinoviev zinoviev mentioned this pull request Jun 17, 2017
@gaearon
Copy link
Contributor

gaearon commented Jun 29, 2017

This looks like it! Apologies, I don't have the time to review right now but we'll try to get back to it. There's just too many things going on at the same time.

@zinoviev
Copy link
Contributor Author

zinoviev commented Jul 9, 2017

@gaearon ping, I've make requested changes and some more additions

@gaearon
Copy link
Contributor

gaearon commented Jul 9, 2017

I'm sorry I haven't looked yet. We got a lot of changes through but this one is complex enough that it keeps slipping through the cracks. I'm adding it to my todos next week, and will try to review. Thank you again for working on this.

@zinoviev
Copy link
Contributor Author

@gaearon ping, any chances for review?

@dbenson24
Copy link

@gaearon any chance this can get reviewed sometime?
#390 and #893

@inoyakaigor
Copy link

So what's with the PR? Problem still actual.
@gaearon ping

@Vannevelj
Copy link

Can this get reviewed? It's breaking on an important part of my code that I'd really like to inspect.

@bvaughn
Copy link
Contributor

bvaughn commented Aug 20, 2019

React DevTools has been rewritten and recently launched a new version 4 UI 🎉 The source code for this rewrite was done in a separate repository and is being merged into the main React repo.

Thank you for taking the time to contribute to this extension 🙇‍♂️

Because this PR is for the version code base, I am closing it. If you'd like to contribute to the new extension, please visit the new repository.

@bvaughn bvaughn closed this Aug 20, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants