-
Notifications
You must be signed in to change notification settings - Fork 1.7k
cache registry reverses, completion in address selector #4066
Conversation
Users quickly want all reverses registered in the path, but don't care so much about live data.
Changes Unknown when pulling ad3b89d on jr-reverse-caching into ** on master**. |
test failing. |
it would also be nice to get it listed in there. |
it only autocompletes in the reverse direction. this is what reverse entries are for right? forward (non-reverse) entries don't get autocompleted yet. you should be able to type |
btw: Travis reported a passing test even though Gitlab failed. @General-Beck? |
Now part of #3991. |
Changes Unknown when pulling ba4263e on jr-reverse-caching into ** on master**. |
the point is to autocomplete gavo -> gavofyork:
let me know if there is something not clear about this statement. |
export default class RegistryMiddleware { | ||
toMiddleware () { | ||
return (store) => { | ||
const api = Contracts.get()._api; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be better to add api
passed as argument to the toMidleware
method, or to the constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would actually like to introduce plain functions for middlewares. Having class
es really doesn't give any benefit and the benefit of consistency doesn't outweigh the cost in terms of readability & complexity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed
confirmedEvents = subscribeToEvent(_contract, 'ReverseConfirmed'); | ||
confirmedEvents.on('log', onLog); | ||
|
||
removedEvents = subscribeToEvent(_contract, 'ReverseRemoved'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not subscribe to both events in one filter ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a real reason, except that subscribeToEvent
uses contract.subscribe(eventName)
under the hood, which is a nice abstraction over the low-level filter API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, but could be nice to be able to give an array of events then :) Even better, have one callback per event, and it would call the right one automagically
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So would you change it to use newFilter
internally? I guess it would still be limited to one address?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I think you should be able to pass an Array of events, and on the filter's callback you get the matching event signature anyway, so then it would call the right callback, right?
removedEvents.on('log', onLog); | ||
|
||
timeout = setTimeout(checkReverses, 5000); | ||
interval = setInterval(checkReverses, 20000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should clear the timeouts and intervals before setting up new ones as a general statement. If they get overwritten even once, then you won't be able to clear them... Might want to use a debounce function though (which can be canceled), would be easier than managing those timers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should clear the timeouts and intervals before setting up new ones as a general statement.
Totally agree! Just didn't pay attention to the fact that startCachingReverses
might be fired twice. 😛
Might want to use a debounce function though (which can be canceled), would be easier than managing those timers.
How would it be easier?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's just to take into account a scenario where this gets called every 2 seconds let's say, thus clearing the timeouts and setting up a new one in 5 seconds, that would never get called
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the way, why waiting 5 seconds at first ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I must admit this is kind of premature optimisation. It exists to reduce the load when the UI just loaded, as completion in the address selector is async anyway and far less important than say account balances.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay yes. Might be worth remembering this when working on the local storage cache issue :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For reference: #4096.
const addr = Object | ||
.keys(this.reverse) | ||
.find((addr) => { | ||
if (addr.toLowerCase().slice(0, query.length) === query) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new RegExp('^' + query, 'i').test(addr)
?
} | ||
|
||
const name = this.reverse[addr]; | ||
return name.toLowerCase().slice(0, query.length) === query; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
name
might be undefined
right?
One question : why using a middleware here? (except to hook to the |
@@ -194,6 +239,8 @@ export default class AddressSelectStore { | |||
.filter((result) => result && !ZERO.test(result.address)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might actually want to get rid of this.registryValues = [];
line 231, to prevent the flickering when typing an autocompleted name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is dangerous, especially when we have (or add in the future) long-running queries, as it will show incorrect values to the users for a while. I'd rather introduce a loading flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm yes that's true. The flickering is a bit odd, but no biggies
.instance | ||
.reverse | ||
.call({}, [ address ]) | ||
.then((reverse) => store.dispatch(setReverse(address, reverse))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might want to make sure that the reversed addressed actually changed ; Redux dispatches can be costly as they might re-render all connected Components.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might want to make sure that the reversed addressed actually changed;
Agree, keeping the nr of actions low is a nice thing.
Redux dispatches can be costly as they might re-render all connected Components.
It is not the job of Redux to make React performant. I'd rather fix the riffing/update logic in our components.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be another solution to let the reducer return the state
directly. A future, less premature optimisation, would then be to use immutable state trees, which check oldState === newState
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Immutables would be really nice actually, not only in Redux. We had some issues with data being modified where it should not have been... And the thing is that at each action dispatch, the mapStateToProps
will get called. So even if this returns non-updating results, it would still be nice to keep these calls low.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In private development, I use seamless-immutable
, which only Object.freeze
s in dev mode to stay fast in production mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's an interesting idea ! Would be worth trying IMO
@@ -36,6 +36,7 @@ export default class AddressSelectStore { | |||
if (query.length === 0 || query === '0x') { | |||
return null; | |||
} | |||
const startsWithQuery = (s) => new RegExp('^' + query, 'i').test(s); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Looks good to me, except the little flickering when typing a name. What about something like this: diff --git a/js/src/ui/Form/AddressSelect/addressSelectStore.js b/js/src/ui/Form/AddressSelect/addressSelectStore.js
index 1c93088..50e02ba 100644
--- a/js/src/ui/Form/AddressSelect/addressSelectStore.js
+++ b/js/src/ui/Form/AddressSelect/addressSelectStore.js
@@ -228,10 +228,12 @@ export default class AddressSelectStore {
});
// Registries Lookup
- this.registryValues = [];
-
const lookups = this.regLookups.map((regLookup) => regLookup(value));
+ const timeoutId = window.setTimeout(() => {
+ this.registryValues = [];
+ }, 200);
+
Promise
.all(lookups)
.then((results) => {
@@ -241,6 +243,8 @@ export default class AddressSelectStore {
.then((results) => {
results = uniqBy(results, (result) => result.address);
+ window.clearTimeout(timeoutId);
+
this.registryValues = results
.map((result) => {
const lowercaseAddress = result.address.toLowerCase(); This would remove the values if the results are not fetched after 200ms. |
#3991