What's the best way to propagate data through your Vue3 App? - ajax

Is there a recommended way of how to propagate data through your vue components? What I'm trying to do is get the data from backend once and propagate it everywhere in my project but I can't find the right strategy.
sessionStorage: Works great and resets on refresh/close window but as soon as you need to create target="_blank" anchor tags, it will not propagate your data to new tabs.
localStorage: Requires in my opinion more work than sessionStorage because you need to delete data manually to keep things tidy. One big problem for me is that it looks like you can't pass markdown and arrays properly, at least without stringify. I've built a project with localStorage and ended up sending ajax requests from most of my components because I couldn't propagate the data through my app how I wanted. At this point my frontend is backend.
My personal problems with localStorage: I am using the marked package to display Markdown but it throws errors if passed undefined. This gets problematic, when I want to use it in a reactive state because instead of resulting in undefined, it throws an error and crashes the whole app. The point I am trying to make is that when you pass an undefined localStorage value to marked in an either or field like so:
const state = reactive({
value: marked(localStorage.value) || ""
})
it crashes your app, if localStorage.value is empty.
Another problem is that I fetch text content depending on a locale and store it in localStorage. This is great until the user changes locale and all content strings have to be replaced by the translated strings. It gets really tricky, if I want to use one component as template to load in different locales.
vuex: I've tried vuex shortly and found it useful but didn't see the benefit over just using localStorage for my purposes. Prolly I'll give it another go.
How do you propagate data through your app?

There are a few good arguments why Vuex is better than Local Storage:
https://www.quora.com/What-is-the-benefit-of-using-Vuex-over-LocalStorage-to-store-the-state-of-an-application-in-Vue-js
You can also try composables. They are reusable functions (similar to mixins) in composition-api (you need composition-api plugin in vue2, in vue3 it is built-in). It can be also the place you store your data. It can be easier and more intuitive than Vuex.
First, create directory /composables and add javascript file (it's
good practice to create file beginning with use word) useState.js:
import { reactive, toRefs } from "vue";
const state = reactive({
isMenuOpened: false
});
const toggleMenuState = () => {
state.isMenuOpened = !state.isMenuOpened;
};
export default {
...toRefs(state),
toggleMenuState
};
toRefs converts all of the properties, to a plain object with
properties that are refs
Now you can use composable in vue components:
<script>
import useState from "./composables/useState";
export default {
setup() {
const { isMenuOpened, toggleMenuState } = useState;
return {
isMenuOpened,
toggleMenuState,
};
},
};
</script>
Demo:
https://codesandbox.io/s/happy-chandrasekhar-o05uv?file=/src/App.vue
About composition api and composables:
https://v3.vuejs.org/guide/composition-api-introduction.html

Since you have mentioned local storage and session storage, I believe you must be trying to share your state across tabs/windows rather than just different components on a single page. At this scale I don't think this is necessarily a VueJS specific issue/pattern. Generally speaking, you want data to be shared pretty much across process boundaries.
Session Storage used to be one of the most sensible ways because it is shared between one window and all the child windows it has created, until all of them are closed at which point the storage will be discarded as well. However, depending on your use cases, Chrome (within the past year) made a change to NOT inherit the session storage from the original window if the popup windows is opened as noopener, hence if you are relying on the noopener (and its performance implications), session storage is no longer usable for this purpose.
Vuex does not solve this issue neither. In fact, Vuex is pretty much irrelevant here. Given the application architecture implied, the state management capability Vuex brings to your app will be likely redundant because any state mutation will probably be submitted to your backend anyway. In some sense the Vuex store is on your backend rather than your frontend.
So we typically do one of the three approaches:
directly broadcast from backend to all frontend tabs. E.g. there is no state sync-ing directly between frontend tabs. Every single tab (child window) communicates directly with the server: it mutates the state by submitting actions to the server, and only the server can change the state and broadcast the changes back to all the tabs in real time (again, conceptually it feels like the Vuex store is on your backend)
use SharedWorker. SharedWorker is shared by all the browsing context with the same origin. It is activated the moment the first browsing context (of a certain origin) is created, and is kept alive until the last browsing context is destroyed. In some sense its sharing semantic is similar to that of the old session storage. You can use SharedWorker as the single entity to communicate to your backend. States can be maintained by the SharedWorker and accessed from the tabs in a RPC fashion. Or states can be maintained separately in each tab and SharedWorker just broadcast the changes to the tabs.
if you actually do not have a backend, but you just want to build multi-window single page application, you can make one of your tabs special and act as the owner of the state store. For all the child windows created from this "master" window, their local store will be a proxy - the actions they perform against the local store will be proxied over to the master window; and the master window performs the action in its store, and broadcast the changes to all the child windows.
By the way, I have used the word "store" many times, but I do not necessary mean the Vuex store. The store is just a shared place where you keep your state.

Related

What is the best practice to get the required data into VueJS component?

I'm building my first VueJS application which is intended to be used by hundreds of people in the future. I tried to make the individual components reusable and indpendent as possible. To achieve this i decided to let every component fetch its required data themselves. This works fine but i'm not sure if its best practice. I could also pass the data between the components or even using the 2-way data binding functionality.
The sketch bellow describes one of the situations i have. Note that 1 account has 1..* users. As you can see i need to fetch the accounts to display them in the accountOverviewComponent.
Currently i only fetch the accounts in the accountOverviewComponent and fetch the users when the account edit button by the passed accountId in the accountOverviewComponent is clicked. This way i don't fetch data i don't need at the time.
I can also include the users (god knows which data/relations will be added in future) to the fetch account response as wel so i can pass all required data to the accountShowComponent when a account edit button is clicked. This way i can save requests to the server with the side note that i fetch users of accounts i dont need. A possible disadvantage is that the account is updated in the accountShowComponent and not in the accountOverviewComponent (for example when the accountShowComponent is a modal on top of the accountOverviewComponent. So i need to pass the updatet account back or re-fetch the accounts after a save or something.
As third option I can do the same in option 2 but than with the 2-way data binding which handles the data synchronization between the components. This will probably restrict the usage of the accountShowComponent to cases where the accountShowComponent is used "on top" of a parent which contains the data.
I can also store the data in a Vuex store and update the stores all the time. I read that this is bad practive as it should be only used for data which is required accros the SPA. I think Vuex is overkill in "simple" situations like this?
What is the best practice of the described situation? I have a bunch of comparable situations/scenarios in my application. Performance (also for mobile devices), scalability and being "future proof" (extendability/modularity) are important for me. Can someone help me out because i'm a bit lost in the options i have?
UPDATE
The reason i think Vue is overkill is comming from this article which makes totally sense from a software engineer perspective to me (i may be wrong). As my components have a kind of "parent - child" relation so i can solve my "issue" easily with passing data (or use 2-way data binding) and callback-events.
The number one use case for storing data in a centralized store like Vuex, is, because the data must be accessible in multiple places of your application, by components which oftentimes are not related in any way (they neither are parents or children of each other). An example of this would be certain user settings to configure how your application looks or what date format should be used, to name a concrete example.

crudGetList action does not delete from state data not present in response

WARNING: This question is specific to react-admin framework
I'm trying to do an in app manual, that uses data from server to load content pages. To doing so I'm doing a custom page that fetches manual pages on componentDidMount. In this function I call react-admin crudGetList(resourceName, pagination, sortingById, filters), where filters is {and:[{condition},{language: currentLanguage}]} since I want to have the manual in different languages. I noticed that having pages in different languages in database and using crudGetList action with filters fetches the correct instances, however the state maintains old data. For example if I initially fetch data in English language, change language and go back to manual page, redux state will have pages for both languages instead of the current selected one.
Is this expected behaviour? Making the new request for manual pages shouldn't replace redux-state data to data coming from request? If is not expected should I open an issue?
React-admin uses a pattern called optimistic rendering. That means that if the app has fetched some entities in the past, if it needs to display these entities, it first shows the stale entities, then fetches the backend, and if the response differs, re-render the screen with up to date data.
For instance, when a user fetches a list of posts, react-admin stores these posts in a dictionary indexed by id:
{
123: { id: 123, title: "hello" },
456: { id: 456, title: "world" },
...
}
React-admin also stores the list of identifiers that the list should display:
[123, 456, ...]
Using these two properties, react-admin can now display the list. But it can also display the detail of a post without hitting the server first. So when a user clicks on an item in the list, react-admin uses the data from the first structure to display it right away, without waiting for the server response.
The purpose of optimistic rendering is performance: since the user doesn't need to wait for a round trip with the server, the interface is super snappy.
In your particular case, I understand that this can cause problems, because the store contains stale data that is not in the desired language. I suggest that you create a custom saga, which reacts to the language change action, and clears the store to avoid this kind of problem.
Check the documentation for custom sagas in the react-admin site:
https://marmelab.com/react-admin/Admin.html#customsagas
You have to configure how the redux store responds to new incoming data.
More specifically, this is what a "reducer" is for; your "action" (in your case crudGetList) feeds the data into the "reducer", which is just a function with instructions to the store on how it should adjust its shape based on the new data.
Somewhere in your app there's probably a reducer that responds to your fetch action, but it's configured to just shove the new results alongside the old, rather than replace them. It's very difficult to know, however, without seeing the code describing the entire redux "cycle".
The redux docs are excellent. I'd start there and make sure you have a good understanding if the entire flow of data through redux, and then go hunting for that reducer.
https://redux.js.org/basics/reducers

In Vue JS, how do I store the "data" (true/false) of a component on a page refresh?

Using Vue Js with Laravel I would like to keep open an expanded nav bar when the user refreshes the page (if they have chosen to open it initially).
The nav bar open/close status is stored in the component's data with a true/false boolean.
I'm a bit confused of which approach to take as have researched various options, so seek the best advice as a noob. Ideally there is a simple way to have the 'data' persist in my component rather than it getting re-rendered to the default of false! Therefore I guess it needs storing in the user's "session" state locally right?
But what do I use and how?
sessionStorage
localStorage
Vuex - https://v2.vuejs.org/v2/guide/state-management.html
a plugin - https://github.com/vuejs/vuex
Laravel session - https://laravel.com/docs/5.5/session
Thanks.
Vuex with a plugin such as this one if your application is large/is growing in complexity. Its a bit of a learning curve though so if you only have a few things to keep track of their states, then localstorage would be a good solution.
Localstorage has the advantage of being easy to use, widely adopted and if you need to in the future, it integrates nicely with state management systems like vuex. The api is quite simple, really just getItem and setItem for common simple use cases. It persists across browser sessions as well.
https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
Sessionstorage is roughly equivalent to localstorage with the key difference being once you close the browser, it gets wiped.

Meteor load static data once for several templates

I'm working with meteor and FlowRouter. I have a collection of a country's administrative divisions and the data is about 2000 documents. I read this data in several routes so at the moment I'm subscribing to the same collection every time I visit one of the routes that is using this data.
This is causing a slow performance and a waste of resources. Given that this collection doesn't change, is there any way to load or subscribe to this data once and have it available for the whole app or specific routes?
Maybe save the data in settings.json and have it available as an object would be better?
Thanks in advance for any help.
You need to keep the subscriptions active between routes. You can do this using this package (written by the same author as FlowRouter so it all works nicely together):
https://github.com/kadirahq/subs-manager
Alternatively, create a Meteor method to return the data and save it in your Session. In this case it won't be reactive, so it depends on your needs.
Any subscription you make that's external to the routing will be in global scope, which will then mean that data from that subscription is available everywhere. All you need to do is set up the subscription say in the root layout file for your site and then that data will be kept in your local minimongo store at all times.
The Todo list collection in the Todo app example here is an example of this, this is the code from that example:
Tasks = new Mongo.Collection("tasks");
if (Meteor.isServer) {
// This code only runs on the server
Meteor.publish("tasks", function () {
return Tasks.find();
});
}
if (Meteor.isClient) {
// This code only runs on the client
Meteor.subscribe("tasks");
You can then query that local data as you would normally.

Alt data dependency between actions not stores

I have a react app where I'm using alt for the flux architecture side of things.
I have a situation where I have two stores which are fed by ajax calls in their corresponding actions.
Having read the alt getting started page on data dependencies it mentions dependencies between stores using waitFor - http://alt.js.org/guide/wait-for/ but I don't see a way to use this kind of approach if one of my store actions is dependent on another store action (both of which are async).
If I was doing this inside a single action handler, I might return or chain some promises but I'm not sure how to implement this across action handlers. Has anyone achieved this? or am I going about my usage of ajax in react the wrong way?
EDIT: More detail.
In my example I have a list of nodes defined in a local json config file, my node-store makes an ajax request to get the node detail.
Once it's complete, a different component (with a different action handler and store) wants to use the node collection to make an ajax query to different endpoints a node may expose.
The nodes are re-used across many different components so I don't want to roll their functionality into several different stores/action handlers if possible.

Resources