Reputation: 7
I'm new into the Flux structure, and I'm trying to change my application to use it, since it appears to be logical and efficient. As much as I understand the logic, I'm having trouble applying it in the real life.
Say I have a fictional TV Shows SPA, using vue, vue-router and vuex.
A Series Page is a component, and inside it I have a section that contains that show's seasons (each one is a SeasonPoster component) and if a season is selected (clicked) it displays all episodes for it.
I have a module series.js for the state of this page (that contains the series info retrieved from the API) and the corresponding mutations and actions.
Where should I store the logic for the season/seasons episodes? I've tried creating a module seasons.js for it but it seemed unnecessary, I've tried putting it inside series module, but it didn't fit well, for example:
This is not the first time I encountered logic/application issues and faced the "where to put this" problem, so if anyone have general thoughts or resource I can use to open my mind about this and the responsibility of each file/component, it'll be much appreciated!
Upvotes: 0
Views: 137
Reputation: 21485
It's a little bit difficult to follow exactly what you're doing without being able to see any of it, but it sounds like you've slightly misunderstood the purpose of vuex; instead of treating it as a data store for the application as a whole you're treating it as a store for the state of what's currently being displayed to the user, and overwriting that store instead of extending it when the user's view changes.
When the user clicks a poster, it dispatches an action setSeason that commits to state.selectedSeason, get the episodes from the API and commit to state.selectedSeasonEpisodes and shows them on the page. But, when I go to another series' page, it's still there.
A much more useful approach would be for vuex to store each season's or episode's data by its ID, and have the component tell vuex which ID it wants data for. When you overwrite different seasons' data into the same 'selectedSeason' variable, you're defeating the purpose of using vuex.
In other words, your vuex store for Seasons looks like
{
selectedSeason: {/* season data */}
}
It should look like
{seasons: [
seasonFoo: {/* season data */},
seasonBar: {/* season data */}
]}
Clearing the state.selectedSeasonEpisodes and state.selectedSeason manually seems like a "workaround" for something I'm not getting. Should I had put it in there in the first place?
Right -- that logic (the "which season am I displaying?") should be in the component; it gets the season ID from (presumably) the route or another component prop, then asks vuex for the data for that particular ID. The vuex store shouldn't need to care what's currently being displayed to the user.
I've tried putting "selectedSeasonEpisodes" and "selectedSeason" inside the state.series (that have the series info and get cleared everytime a new series page is entered), but I'm getting a lot of errors, as when the series is not loaded yet its value is "null" and properties are undefined.
For asynchronous data calls, there's going to be some delay before the data returns, so your components need to be able to cope with null data (either displaying a loading spinner or empty fields.) When the vuex state updates, the component should pick up the correct data from it automatically (because the component is bound to the vuex state).
I thought about creating a seasonsEpisodes (plural) instead of selectedSeasonEpisodes that adds up as users click on seasons, sort of like a "cache"
Vuex is that cache; that's why vuex exists. If a component requests data that vuex already has, it can return it immediately; if it requests data that vuex doesn't have yet, vuex can request it from the server. (This is part of why you want to decouple your vuex store from the user's current view; if vuex only contains what's currently in use and overwrites old data on every request, you may as well just skip vuex altogether and have each component request data directly from the server.)
In the above method it displays only the selected season episodes by .find, but the initial state of seasonsEpisodes is undefined so it also generates an error. Should I manually define the initial state as an empty array?
As above, it's best to write your components in such a way that they can cope with null or empty data, because that situation will definitely happen while data is being requested asynchronously from the server. How best to do this depends on your particular implementation; you might init with an empty array so the find
doesn't throw an error, or you could defer trying to find
until after the data arrives.
Upvotes: 1
Reputation: 18146
It's totally up to you how you organize your storage, Vue doesn't limit your options. This is important to understand, as you don't need to follow a specific scheme if you don't need to. Create a storage which makes sense for your app.
Probably the only strict thing is async tasks can be done in actions only, and a rule of thumb is that state data is accessed by getters, and changed by mutations (avoid direct access, though you're not obligated).
If you ask for an advice:
in similar situation I store everything fetched from API inside a storage to prevent repeating API calls. App doesn't get slow because of this, unless you perform some crazy inner logic frequently (you probably don't), and user immediately gets previously fetched information.
Also, you can avoid 'empty' errors by checking existence with v-if
and defining state
arrays/objects as empty arrays/objects. I don't see a problem here.
When a user clicks another series link, you can check if this info already exists in your state (fetched earlier), if yes - show it immediately, if no - show loading animation while fetching data and pushing to your cache array/object.
Upvotes: 0