-
Notifications
You must be signed in to change notification settings - Fork 14.4k
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
fix: Owners selection in dataset edit UX #17063
Conversation
d24364f
to
9c92b7e
Compare
9c92b7e
to
51e6c3b
Compare
Codecov Report
@@ Coverage Diff @@
## master #17063 +/- ##
==========================================
- Coverage 76.89% 76.67% -0.22%
==========================================
Files 1031 1031
Lines 55183 55188 +5
Branches 7505 7505
==========================================
- Hits 42432 42316 -116
- Misses 12499 12620 +121
Partials 252 252
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
@@ -374,6 +374,40 @@ const defaultProps = { | |||
onChange: () => {}, | |||
}; | |||
|
|||
function OwnersSelector({ datasource, onChange }) { | |||
const selectedOwners = datasource.owners.map(owner => ({ |
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.
will datasource and owners always have a value?
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 should always have value since the top level component has datasource as is required:
https://github.com/apache/superset/pull/17063/files/51e6c3b96222397059051a082d8fe257bd2bc788#diff-a17a45fe0d0a8a3079cfe67ede4def65af02cef20ba94e9ee827b62edf51df23R367
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.
ok, great. then per the below comment, it looks like selectedOwners will never be null or undefined, so you don't need the || []
ariaLabel={t('Select owners')} | ||
mode="multiple" | ||
name="owners" | ||
value={selectedOwners || []} |
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.
will selectedOwners ever be null? Unless there's an error above in the map, this value will already default to []
value={selectedOwners || []} | ||
options={loadOptions} | ||
onChange={newOwners => { | ||
onChange(newOwners); |
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.
you should be able to just pass onChange as the function instead of writing a new one
})), | ||
totalCount: response.json.count, | ||
})); | ||
}); |
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 believe if you don't pass any dependencies to useMemo, it will still recalculate on every rerender. Do you need the useMemo here? It looks like all it will do on rerender is create the query variable.
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 some reason the select component fails with out the useMemo
we should look into that more but lets leave it for now
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'm going to push back here... let's try to understand why useMemo
is needed? My understanding is the same as Elizabeth's, without the dependency array it's a no-op.
We've had a lot of quality issues lately, let's not commit code that we don't understand what's doing.
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.
Talking to @michael-s-molina about useMemo vs. useCallback, if you fire the function with the same parameters (search, page, pageSize in this case) this code won't re run. So it is better for us to use useMemo
since we'll be having the parameters for populating the select with owners options.
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 believe if you don't pass any dependencies to useMemo, it will still recalculate on every rerender.
You're correct. If no array is provided, a new value will be computed on every render. In this case, we are passing a function to the options
prop of the Select and this prop is used by useEffect
hooks which means we don't want to recompute it unless the dependencies change. We could use a useCallback
here but we also don't want to execute the function if it receives the same parameters. That's why we generally use useMemo
with a dependency array (sometimes empty, sometimes not).
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.
Just to let you know, we are already working on the documentation for each prop of the component and the different modes of operation. We're also going to update the Storybook with these docs.
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, I just left 2 small nits.
onChange={newOwners => { | ||
onChange(newOwners); | ||
}} |
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.
Nit:
onChange={newOwners => { | |
onChange(newOwners); | |
}} | |
onChange={onChange} |
const loadOptions = useMemo(() => (search = '', page, pageSize) => { | ||
const query = rison.encode({ filter: search, page, page_size: pageSize }); | ||
return SupersetClient.get({ | ||
endpoint: `/api/v1/chart/related/owners?q=${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.
The result is the same, but let's use dataset
here instead of chart
:
endpoint: `/api/v1/chart/related/owners?q=${query}`, | |
endpoint: `/api/v1/dataset/related/owners?q=${query}`, |
/testenv up |
@yousoph Ephemeral environment spinning up at http://35.86.147.0:8080. Credentials are |
/testenv up |
@hughhhh Ephemeral environment spinning up at http://34.209.136.120:8080. Credentials are |
/testenv up |
@hughhhh Ephemeral environment spinning up at http://34.217.1.111:8080. Credentials are |
/testenv up |
@hughhhh Ephemeral environment spinning up at http://54.201.92.110:8080. Credentials are |
superset/connectors/base/models.py
Outdated
"owners": [ | ||
{"label": f"{owner.first_name} {owner.last_name}", "value": owner.id} | ||
for owner in self.owners | ||
], |
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.
label
and value
are concerns tied to the component you're using in the frontend. Let's discuss this a bit more.
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 assume we have endpoints where we can get full user information, including first name and last name. The component should fetch from there and build label
and value
however it needs.
})), | ||
totalCount: response.json.count, | ||
})); | ||
}, []); |
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.
Curious to what the error is when you just use the function directly?
Using useCallback
is basically the same as useMemo
, though now you have an empty array of dependencies so it will memoize forever, which is what we want.
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 had the wrong function schema 😓 rookie move
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.
Let's clarify the concepts a little bit (https://reactjs.org/docs/hooks-reference.html)
useCallback
will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders.useMemo
will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.
Curious to what the error is when you just use the function directly?
If we pass the function directly, this means that every time the component renders, it will create a new instance of the function, which will trigger the useEffects
inside the Select component, which will call the function and execute it unnecessarily.
If we use useCallback
it means that the function instance will be the same between renders, avoiding unnecessary executions. But if while interacting with the component, this function is called two or more times with the same search
, page
, and pageSize
, useCallback
won't prevent unnecessary executions. That's why we use useMemo
. One example of this scenario is if we change a property like useFetchOnlyOnSearch
which will trigger a new call to the function with the same parameters.
🏷️ 2021.40 |
Thanks! I will update the the pr description accordingly. 🙏 |
Ephemeral environment shutdown and build artifacts deleted. |
* boilerplate * update owner select component * this is working * update onchange * refactorig * you need to useMemo or things break * update test * prettier * move logic into bootstrap data endpoint * address concerns * oops * oops * fix test (cherry picked from commit 959fd76)
* boilerplate * update owner select component * this is working * update onchange * refactorig * you need to useMemo or things break * update test * prettier * move logic into bootstrap data endpoint * address concerns * oops * oops * fix test (cherry picked from commit 959fd76)
* boilerplate * update owner select component * this is working * update onchange * refactorig * you need to useMemo or things break * update test * prettier * move logic into bootstrap data endpoint * address concerns * oops * oops * fix test
SUMMARY
The original dataset edit screen didn't populate owners and wasn't allowing users to properly query the db for potentially new owners.
BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
edit-owners.mov
TESTING INSTRUCTIONS
ADDITIONAL INFORMATION