-
Notifications
You must be signed in to change notification settings - Fork 21
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
[DC-875] Debounce Text Input and Slider #4714
Conversation
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 looks OK, although it makes me wonder about the general problem we're solving here.
The reason we need to debounce is because eventually we end up calling a web API that's known to be slow. So another approach is to instead only wrap the web API itself in a debounce operation. If this approach works then we wouldn't have to add another debounce when a new UI element is added.
So, debouncing DataRepo itself? Would this make every API endpoint debounced? |
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 great!
Are we creating a problem for the user by not providing immediate visual feedback? |
I was thinking the Also I think we'd want to provide both bounced and non-bounced versions of the API, in case a caller wants to compute the value right away. Otherwise any call would force the caller to wait for the bounce period (250ms) to expire. I'm still not 100% sure that this approach will work.. it looks like there are some utilities that can be used to debounce a promise returning function, which the API calls are, so I'm hopeful that it might. For example https://www.npmjs.com/package/debounce-promise |
src/dataset-builder/CohortEditor.ts
Outdated
}) | ||
); | ||
}, [criteria, datasetId, setCriteriaCount]); | ||
const debounceSetCriteriaCount = useRef( |
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.
we want to use a useRef here because we only want one instance of the debounce
function. If there are changes to any dependency inside of useRef (i.e datasetId, criteria) we can change it with .current()
and passing in new values.
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 seems OK although I wonder if some refactoring and renaming could make the code clearer. Also, there is a third call to getCounts()
in DatasetBuilder.ts
. Doesn't that need a debounce as well?
Is there a way to unit test the debounce behavior so that we can be sure we don't break it in the future?
@@ -31,6 +31,13 @@ jest.mock('src/libs/ajax/DataRepo', (): DataRepoExports => { | |||
}; | |||
}); | |||
|
|||
jest.mock('src/components/input', () => { |
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.
Is this mock still necessary?
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 thought that since the tests were passing they were needed but I removed the mock and the tests are still passing.
); | ||
}, [criteria, datasetId, setCriteriaCount]); | ||
const debounceSetCriteriaCount = useRef( | ||
_.debounce(250, (datasetId, criteria) => { |
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 it be possible to split out the _.debounce(250, ...)
into a separate method so it could be shared in both places? Also it seems like we'd want a way to mock it during tests, so our tests wouldn't have to wait for the debounce to timeout before an expected text appears in the UI.
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.
If we were to do this, we could get rid of the useRef because useRefs are only allowed to be used in a component. Then, we can create an abstract function to get the debounce out.
const debounceFunc = _.debounce(250, (func) => func);
Then, we would put the func
inside of the useEffect
like this:
useEffect(() => {
debounceFunc(
setCriteriaCount(
async () =>
await DataRepo()
.dataset(datasetId)
.getCounts({
cohorts: [
{
// Create a "cohort" to get the count of participants for this criteria on its own.
criteriaGroups: [{ criteria: [criteria], name: '', meetAll: true, mustMeet: true }],
name: '',
},
],
})
)
);
}, [criteria, datasetId, setCriteriaCount]);
There could certainly be a stronger function name for debounceFunc
, but I personally think this will clutter the useEffect
? We can't set this function:
setCriteriaCount(
async () =>
await DataRepo()
.dataset(datasetId)
.getCounts({
cohorts: [
{
// Create a "cohort" to get the count of participants for this criteria on its own.
criteriaGroups: [{ criteria: [criteria], name: '', meetAll: true, mustMeet: true }],
name: '',
},
],
})
)
as a variable outside of the useEffect
because this is going to cause an infinite render error and we also can't put it inside of the useEffect
because this will be undebounced.
src/dataset-builder/CohortEditor.ts
Outdated
}) | ||
); | ||
}, [criteria, datasetId, setCriteriaCount]); | ||
const debounceSetCriteriaCount = useRef( |
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.
The name doesn't seem right to me. This is really debouncing the call to getCounts()
; the fact that it also debounces setting the value once that API returns seems incidental, as debouncing the set operation has no effect (vs debouncing the API call, which does).
Maybe a better name is updateCriteriaCount
which describes what this function is doing instead of how it's doing it?
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 agree that we aren't really debouncing setCriteriaCount but more calling debouncing getCounts();
and then setting it.
I don't think we have to debounce the |
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 looks great thanks for the comments and videos!
…hub.com/DataBiosphere/terra-ui into es/dc-875-debounce-year-of-birth-slider
Quality Gate passedIssues Measures |
Jira Ticket: https://broadworkbench.atlassian.net/browse/DC-875
Summary of changes:
What
Why
Testing strategy
Before:
before-debounce-year-slider.mov
After:
debounce.text.input.and.slider.mov