-
Notifications
You must be signed in to change notification settings - Fork 63
Create a proof-of-concept for Pinia migration #906
Conversation
Please note that the ATTRIBUTION store is being removed in #905 |
Oh yeah, this was just a test to see if things work and if this is the right direction to proceed in, before we start migrating some of the more massive modules. |
@dhruvkb this looks excellent, great initial proof of concept. I'd love to see this followed up with a Pina RFC next week to outline further next steps in more detail. |
We should also make a note to remove 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.
I'd love to see adding a requirement be TypeScript types for each re-written store. To include this we'd also have to add each re-written store to the tsconfig.json
includes
list.
This is great though!
In this stage we must determine if we must use mutations as actions. Pinia allows changing the state directly in the components (which was frowned upon by Vuex) so we could do away with mutations in theory. But having them and following the Vuex principle makes transition smoother and keeps state changes limited to one file instead of scattered around the codebase.
I'd be nice to stick mutations in the Pinia store, if only because it'll make them significantly easier to test without having to rely on triggering them via some component interaction.
in the test case
We can really just call the useX
store composable inside the test function like that? Vue won't complain about using composables outside of a component context?
In your time researching, did you come across any opinions about whether to use the object or function callback syntax for defining a store? I strongly prefer the function callback syntax because I feel like it more closely mirrors the way state is created in setup
(in fact, it's exactly the same) and it also makes having private helper functions possible (compared to the object syntax where all the functions on the definition object will be public). It also eliminates this
usage which I've generally found to be a good thing for comprehensibility in JavaScript generally.
# Conflicts: # src/locales/po-files/openverse.pot
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. My only remaining question is if we want to use the useNav
naming convention or be extra redundant and clear with useNavStore
, but that's a very minor concern we could change later.
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 can't approve this PR so please accept ✅ .
I moved 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.
As mentioned by @zackkrida elsewhere there are errors in the app when trying to run it locally. None of the comments I added are blocking I don't think.
One thing I'd like to see is adding the two new stores to tsconfig.json
(or updating them to be .ts
). I tried this locally (converting to .ts
) and there were only three errors to fix in active-media.ts which just came down to moving JSDoc parameter type annotations into TS annotations.
@@ -1,3 +1,6 @@ | |||
// WebStorm fix for `~` alias not working: | |||
// https://intellij-support.jetbrains.com/hc/en-us/community/posts/115000771544-ESLint-does-not-work-with-webpack-import-resolver-in-2017-3 | |||
process.chdir(__dirname) |
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.
Wow! What a weird find.
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.
Yes, definitely. It seems that the bug reports are from 2018, but I'm having this problem now 🤷
* Only the properties used by components are exported as refs. | ||
* `status` is not used anywhere in the components. | ||
*/ | ||
const { type, id, message } = toRefs(state) |
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 couldn't find any usages of activeMediaStore.type
. id
and message
both have usages though.
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 type
will be used when we have video, and we can have an activeVideo
which will need to be treated differently from activeAudio
src/stores/active-media.js
Outdated
type, | ||
id, | ||
message, |
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.
Should these all be readonly
as well to prevent modifying them without going through the action functions?
src/stores/nav.js
Outdated
export const useNavStore = defineStore(NAV, () => { | ||
const state = reactive({ | ||
isEmbedded: true, | ||
isReferredFromCc: false, | ||
}) | ||
const { isEmbedded, isReferredFromCc } = toRefs(state) | ||
|
||
function setIsEmbedded(isEmbedded = true) { | ||
state.isEmbedded = isEmbedded | ||
} | ||
function setIsReferredFromCc(isReferredFromCc = true) { | ||
state.isReferredFromCc = isReferredFromCc | ||
} | ||
return { | ||
isEmbedded, | ||
isReferredFromCc, | ||
setIsEmbedded, | ||
setIsReferredFromCc, | ||
} | ||
}) |
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 looks like we use a mix of updating the isEmbedded
and isReferredFromCc
values directly and also through the setter functions. Should we standardize on one approach for simple properties like this?
navStore.setIsReferredFromCc(false) |
navStore.isReferredFromCc = query.referrer.includes('creativecommons.org') |
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 prefer setter functions because they are easier to observe.
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 went with setter functions is there a way to enforce their usage, like to make isReferredFromCc
readonly?
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.
Yes! I'll do that
|
||
describe('Active Media Store', () => { | ||
beforeEach(() => { | ||
setActivePinia(createPinia()) |
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.
Does this have the effect of resetting the store before each test?
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.
Yes:
// creates a fresh pinia and make it active so it's automatically picked
// up by any useStore() call without having to pass it to it:
// useStore(pinia)
Co-authored-by: sarayourfriend <[email protected]>
Reconfirming that this looks good! LGTM. |
# Conflicts: # src/components/VFilters/VFilterChecklist.vue # src/locales/po-files/openverse.pot
Migration to Pinia
Feel free to respond to this PR/RFC/proposal with
Goals
Side-goals
useStore
helper in favour of modularuseX
from Pinia storesSteps
Install Pinia and test compatibility (this PR)
Replace Vuex stores with their Pinia counterparts
state -> state
getters -> getters
mutations and actions -> actions
In this stage we must determine if we must use mutations as actions. Pinia allows changing the state directly in the components (which was frowned upon by Vuex) so we could do away with mutations in theory. But having them and following the Vuex principle makes transition smoother and keeps state changes limited to one file instead of scattered around the codebase.
beforeEach
in the test case
~/constants/mutation-types.js
~/constants/action-types.js
~/constants/store-modules.js
-1. In the end, with no Vuex stores remaining, Vuex and all associated dependencies will be removed. The
store/index.js
file can also be removed after the complete migration to Pinia. ThedisableVuex: false
flag should also be removed.Conventions
Nomenclature
active.js
→activeStore
/useActive
SET_ACTIVE_MEDIA_ITEM
→setActiveMediaItem
Type hinting
In the interest of a quick migration, we can continue to use the existing type definitions for the state and the various mutations/actions from the
types.d.ts
file. Some definitions in the file may be out of date (as was the case forActiveMediaState
), so a cursory glance to check that would be useful.Procedure
To minimise disruption, each store will be changed using a lock-based approach where the developer takes a lock on the store using a notification to the group, makes a PR updating a single store and its usages and this lock is released after the PR is merged.
This prevents PRs from diverging too much and allows a seamless transition for all involved.
Fixes
Addresses #756 by @sarayourfriend
Addresses #831 by @obulat
PR description
This PR
active
, to PiniaChecklist
Update index.md
).main
) or a parent feature branch.Developer Certificate of Origin
Developer Certificate of Origin