-
Notifications
You must be signed in to change notification settings - Fork 241
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
Migrate the usage of asyncData to fetch #10839
Conversation
48eb5b8
to
68509e6
Compare
30de843
to
506adda
Compare
506adda
to
0b51f10
Compare
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 page should be scrutinized the most during review. I wasn't able to do a 1 to 1 translation between asyncData and fetch primarly due to the previously used mounted()
hook.
While figuring out what the issue was I broke up and shrunk the definition of asyncData and decided to leave it. If you'd like me to go back to a 1 to 1 definition let me know, I don't think it should be an issue.
data() { | ||
const username = this.$cookies.get(USERNAME, { parseJSON: false }) || ''; |
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.
Switching the access pattern of $cookies to be consistent with $route. It doesn't matter here but it matters when defining fetch as the code is treated completely differently by nuxt.
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 would have expected all the data to come from one place only, which is meant to be the state management, while persistent data is to be collected together in one single object. We do not want to tackle this now though, so what you did for me is already totally fine and is a good start.
this.username = this.firstLogin ? 'admin' : this.username; | ||
this.$nextTick(() => { | ||
this.focusSomething(); | ||
}); | ||
}, | ||
|
||
methods: { | ||
async loadInitialSettings() { |
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.
Pulled this out to reduce the amount I needed to understand while mentally parsing asyncData.
this.username = this.firstLogin ? 'admin' : this.username; | ||
this.$nextTick(() => { | ||
this.focusSomething(); | ||
}); |
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 had to be moved from mounted. Previously asyncData would be executed and the state set before mounted was called. This caused big headaches while debugging e2e tests.
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 good you found out, thanks.
There's one aspect related to vendors and providers, which I am not sure we have covered in anyhow, but I guess it should be fine till other related variables are there as well.
if ( hasLocal ) { | ||
// Local is special and handled here so that it can be toggled | ||
removeObject(providers, 'local'); | ||
} |
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 love how we modify const lists. I really think immutable should take place of referentially const practically everywhere.
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.
Our utilities are aimed at doing that, so it may be worth starting the conversation from that point since it's simpler.
For the record, I think me you and @rak-phillip are all in for that and we can even use ESLint for it, including Vue specific cases.
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 all for it. Although I'm unsure if eslint will catch cases like this where we aren't explicitly reassigning the constant and mutating function parameters in removeObject
..
const username = $cookies.get(USERNAME, { parseJSON: false }) || ''; | ||
|
||
return { | ||
product: getProduct(), |
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 wasn't used anywhere in login
this.$set(this, 'productName', productName); | ||
this.$set(this, 'haveCurrent', !!current); | ||
this.$set(this, 'username', me?.loginName || 'admin'); | ||
this.$set(this, 'isFirstLogin', isFirstLogin); | ||
this.$set(this, 'mustChangePassword', mustChangePassword); | ||
this.$set(this, 'current', current); | ||
this.$set(this, 'v3User', v3User); | ||
this.$set(this, 'serverUrl', serverUrl); | ||
this.$set(this, 'mcmEnabled', mcmEnabled); | ||
this.$set(this, 'telemetry', telemetry); | ||
this.$set(this, 'principals', principals); |
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.
Some places we use $set and others we just use assignment. Is one better or easier for migration?
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 are going to remove all the this.$set
in the migration script with simple assignments, although it works differently between Vue2 and Vue3. The only concern is that my regex rule may not capture this case me?.loginName || 'admin'
and will need to be modified by hand, but it's not a big deal.
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.
Oh also that's a good point, no idea if we want specifically some cases to not be reactive, so a consistent assignment is preferred to avoid debugging later on.
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.
$set
should be an indicator that reactivity is desired if we are adding new properties to an already reactive object. I agree that assignment should be the default case, unless we're dealing with change detection caveats of Vue2
0b51f10
to
ddd3bc3
Compare
After the migration we will then remove all of the initialization code. This is motivated by the Vue3 migration.
ddd3bc3
to
7d282bf
Compare
showLocal: !hasOthers || (route.query[LOCAL] === _FLAGGED), | ||
firstLogin: firstLoginSetting?.value === 'true', | ||
singleProvider, | ||
showLocaleSelector: !process.env.loginLocaleSelector || process.env.loginLocaleSelector === 'true' |
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.
Nothing about this code is async so I moved it to data.
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 cannot see any issue or valuable point to discuss that you did not already mention.
A topic for the future out of scope and which I already expressed is the presence of async patterns all the time from the state, which is really not necessary and caused clearly headaches also here.
LGTM
data() { | ||
const username = this.$cookies.get(USERNAME, { parseJSON: false }) || ''; |
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 would have expected all the data to come from one place only, which is meant to be the state management, while persistent data is to be collected together in one single object. We do not want to tackle this now though, so what you did for me is already totally fine and is a good start.
this.username = this.firstLogin ? 'admin' : this.username; | ||
this.$nextTick(() => { | ||
this.focusSomething(); | ||
}); |
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 good you found out, thanks.
There's one aspect related to vendors and providers, which I am not sure we have covered in anyhow, but I guess it should be fine till other related variables are there as well.
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.
Do you think that we'll be able to safely remove these lines about asyncData
now that it's no longer used throughout Dashboard?
dashboard/shell/initialize/App.js
Lines 109 to 118 in 08f11a5
if (page.$options.asyncData) { | |
p.push( | |
promisify(page.$options.asyncData, this.context) | |
.then((newData) => { | |
for (const key in newData) { | |
Vue.set(page.$data, key, newData[key]); | |
} | |
}) | |
); | |
} |
dashboard/shell/initialize/client.js
Lines 382 to 393 in 08f11a5
if (hasAsyncData) { | |
const promise = promisify(Component.options.asyncData, app.context); | |
promise.then((asyncDataResult) => { | |
applyAsyncData(Component, asyncDataResult); | |
if (this.$loading.increase) { | |
this.$loading.increase(loadingIncrease); | |
} | |
}); | |
promises.push(promise); | |
} |
edit: There's quite a few more references to asyncData
in shell/initialize/client.js
, but I don't think it makes sense to list them all here 🙂
As you mentioned we all agree there is a point for another issue and there are more cases, but since we do it I add a couple of comments. There are more components using asyncData: https://github.com/search?type=code&auto_enroll=true&q=%22.asyncData%22+OR+%22+asyncData%22+repo%3Arancher%2Fdashboard Also, let's not forget that something related to the routing can be handled with the related vue-router function. Albeit we may not want to fall in the same trap 😅 |
Absolutely. I took care of that in this PR 3cdf3ec. |
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.
LGTM - I gave the changes a quick smoke test and everything functions as it did before.
if ( hasLocal ) { | ||
// Local is special and handled here so that it can be toggled | ||
removeObject(providers, 'local'); | ||
} |
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 all for it. Although I'm unsure if eslint will catch cases like this where we aren't explicitly reassigning the constant and mutating function parameters in removeObject
..
this.$set(this, 'productName', productName); | ||
this.$set(this, 'haveCurrent', !!current); | ||
this.$set(this, 'username', me?.loginName || 'admin'); | ||
this.$set(this, 'isFirstLogin', isFirstLogin); | ||
this.$set(this, 'mustChangePassword', mustChangePassword); | ||
this.$set(this, 'current', current); | ||
this.$set(this, 'v3User', v3User); | ||
this.$set(this, 'serverUrl', serverUrl); | ||
this.$set(this, 'mcmEnabled', mcmEnabled); | ||
this.$set(this, 'telemetry', telemetry); | ||
this.$set(this, 'principals', principals); |
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.
$set
should be an indicator that reactivity is desired if we are adding new properties to an already reactive object. I agree that assignment should be the default case, unless we're dealing with change detection caveats of Vue2
Summary
Migrate the usage of asyncData to fetch. I'll remove the nuxt implementation of asyncData in a separate pr (3cdf3ec).
This will give us one less thing that we have to consider while working on Vue3. After the migration we will then remove all of the initialization code.
Occurred changes and/or fixed issues
Technical notes summary
Areas or cases that should be tested
The affected pages are, login, logout, setup, auth/config, and auth/roles.
All of these pages appear to be exercised through existing e2e tests.
Checklist