VueJS: change global state through chain of recursive components - javascript

I use recursive component to render nested array in Vue. Also I need to make possible to check any leaf of these tree, render every node as true/false/indeterminate (if some leafs are selected) and store all selected values in some ‘global’ variable. What’s the right way to pass the information and store the states? I think using a reference to the “root” component is not right and creating chain of events through all nested components may be too much?

This is a perfect task for vuex.
It is the defacto store system into the vue ecosystem.
You store all of your data into that store and access and manipulate them with mutations and getters.
And you get the benefit, that everything in your Views stays synced.
Check out their amazing docu at: https://vuex.vuejs.org/en/intro.html

Related

Redux: How to manage conflicting state across routes?

In my application, on route1 I get a list of items from an api call. The component is connected and the items are saved in redux store and passed to the component as props. Now, on route2 I again need this list of items.
For now, i am fetching the list of items again when we move to route2
Does it make sense to reuse the list of items which is already in global state? If yes, how do we decide when it is stale and when to refetch?
If it doesn't make sense to reuse the list of items, should i clear the list when moving away from route1/when loading route2? Because otherwise, my component in route2 cannot start with an assumption that the list would be empty on load, and I would have to put an extra check for whether or not the list is empty. This would make my component kinda aware of the global state structure.
Does it make sense to name these pieces of state differently, so that one doesn't interfere with the other? This doesn't seem right, as both the functionalities are same, and we wouldn't want to increase the size of global state for this.
is redux or any global state management library not a good choice for handling this kind of state? This seems like state local to a route, which should not interfere with local state on another route, so it is not truly global. Should we only keep global state in redux ?
I have previously been using useReducer with axios calls to do this. The state in that case was local to each of the components/route, so they didn't have to care about whether or not the state has been populated by someone else. This approach had problems with sharing state between routes, but local state problem was handled well.
In this situation, I think you should store the item list in the global state as readonly data. When a route component initialize, it should clone this item list from the global state and store it as local state.
This way ensures that the item list data is shared across multiple components without having to refetch it for every route while each component can choose to do whatever they want with its local data without affecting other components. Also, when the component unmounts, it will clean up its local state so you don't have to worry about memory leak due to multiple copies of the item list.
I have been in this situation and the approach I used was as follows:
Keep one state for both routes. Since that's the only point of
keeping a single source of truth across the application. If we want to keep a separate list for both routes then we can directly use state which I think is not required here.
Fetching of the list should be done in componentDidMount of
the first route and in route 2 you can again fetch the list and compare the data based on some checksum if we have new data or not if we have new data update the original list. In this, we don't have
the problem of stale data too.
Maybe you can have a different use case but check if this approach works.

Should derived data be the responsibility of the NGRX store?

Should derived data be the responsibility of the NGRX store through selectors or should it be defined on the component that will use it? For example, let's say you have an object with startTime, itemsCompleted, and itemsRemaining. A utility function calculates some other properties such as expectedLate, expectedOverage, etc. Should that take place inside a selector or locally on the component? Does it matter?
Personally, I like to use selectors for derived data. A component just retrieves data from the store and displays it.
This imho makes selectors and components easier to test.
Following this convention it's also easier for devs to find the information they are looking for.
Also, you have one single source of truth. For example, if a user logs out you just have to update the user instead of updating the user and setting a isLoggedIn flag to false.

How to pass data from a VueJS component to main.js?

Is it possible to send data defined in a component in the data() method to the main.js file (or where the Vue instance is defined)? I thought I could use vueX to do the following but have no idea as how to approach it even after understanding computed props, getters and mutations:
Data in Component -> Store -> Main.js
EDIT: HOW DO I SET DATA IN THE VUEX STORE FROM THE COMP?
Imagine the store has a property called currentList, whose values need to be fed in from a list in Component1.vue. How would I send that list to the VueX Store?
If you want to pass data from your component to the Vuex $store, you should use Mutations (or actions, if the data comes into your component asynchronously).
Assuming your mutation has already been defined in the store (and the store has been set up correctly), you can call it from your component as follows:
this.$store.commit("YOUR_MUTATION", "data")
I do recommend the vue-tools in your browser to keep track of what's in your store.
I know no Vue, but I believe I understood what you're looking for.
What you're trying to achieve here, in every other front-end framework, can be called like: "component to parent communication".
You can have a 4th level nested component and you want to pass some data to the Main.js (that I suppose, in Vue, it's still a component, precisely the root one)
As I see, you can use $emit: https://forum.vuejs.org/t/passing-data-back-to-parent/1201
In React (and you can do this in Vue too, but you should choose the vue style solution) you would use a callback function: the parent pass to the child a function that the child can call to set a certain property inside the parent comp.
Another common option nowadays, that avoids completely this kind of communication, is the use of a state management lib like Redux. In that every component can access a global store (that is like a global variable) and after the 4th level component set a property in this global store, the main.js can read it.

Sharing application store state

Im using Vue and Vuex in my application and I have some components. By the time passing I know I will probably have a few more which is gonna be a lot. So I have a problem with sharing the app state within all the components. So I'd like to know which way should I follow? Either passing the state to all the components no matter child or parent, or passing the state to a parent one that will be used in some of its child components and then passing the state to them as a property (however, I do need to import the mutations methods because props are not reactive)?
UPDATE:
Keep in mind though that if a component is going to be displayed or hidden and the conditional statement v-if is attached to the component custom tag, each time the entire component is going to be rendered again! But not if statement was only on some child tags in the component.
Thanks in advance.
What you are asking is a false dilemma. From the official documentation:
By providing the store option to the root instance, the store will be injected into all child components of the root and will be available on them as this.$store.
(emphasis mine)
Which means that sharing the store between components no matter how deep in the component tree is handled for you automatically by vue and vuex.
It's quite opinion based question, but I would say it is much better way to use Vuex since the beginning if you know that your app can be developed into large scale.
Passing states from parent to child all the way etc is not good way, so it is better to use Vuex now and you will save a lot of time changing your app in the future when you would have no other way than vuex.

Flux store emitting changes to specific react components rather than all components.

Struggling to find or come up with an elegant answer to this one:
If I have multiple dynamic react components that are listening to one flux store to update their child components is it possible to emit changes to specific components rather than emitting changes to all the components that are registered to listen to changes on that store?
E.G: A dynamic component has a button and when clicked its tells the flux store to send some data to API. The dynamic component will it update its child view depending on the response and change emitted by the flux store. But since all the dynamic components are listening to the store they will all update their child views which is the undesired behaviour. Ideally the flux store could identify which component to emit the change to, or the components can identify that change is not for them.
Is this possible? Or does it go against flux principles?
I don't know if it violate flux architecture, but it seems not leveraging some beauties of it.
The beauty of a simple emit change (without change detail) is that a store wouldn't need to have explicit knowledge on views, also, with the React Virtual Dom framework, it shouldn't cost too much performance hit.
To further optimize the performance, you can implement shouldComponentUpdate on your React view (base on the differences in it's own properties), to avoid triggering the tree-diff algorithm.
See this: https://facebook.github.io/react/docs/component-specs.html
== Add more info ==
In more traditional MVC, the model will emit changes to a particular source and with particular details, e.g.
this.emit({
details: { x: 'x', y: 'y' },
source: objectA
)};
The view (or controller) that receive this needs such detail to update it's Dom, you will call the update(changes.details) instead of the initial render() method because Dom manipulation is expensive.
ReactJS 'solved' this by having another virtual Dom layer, which use pure Javascript to compute the 'optimal' differences in Dom manipulation, so in React, you never have a method call update(), you will always call render() base on current state of the view, and React does the optimization for you.
So using Flux with React, your store can just emit change without any details and the views that listen to it can just render with 'optimal' Dom manipulation (so if it's state hasn't been changed, there will be no Dom manipulation).
But of course, you will say in this case React will still trigger the virtual Dom diff computation, which still cost something. So to further optimize it, you can implement shouldComponentUpdate on a view that contains big sub-tree (base on it's own state), to avoid React to run the diff computation.
The beauty of emit a simple change, besides easier code, is that Store can be pretty much decoupled from view.
For example if you trigger specific change details for particular views, then you will need to remove or change code in store(s) when the view is not listening the that store anymore.
It does not go against flux principle but beware not having only one big store, sometime it's better to split in several tiny store.
But I think I understand your use case, one store containing a collection of similar objects (like a backbone collection).
So lets say your store receive a new object or an array of new object (or things to update in your store), you have a register function which will add this object (or update) to your store.
For sure this object has an id field (or something similar). Then for each new object of your array you just received you'll emit the id.
And your view are binded to their id as change event. Basically you use your store like an array, when the array is change you emit the key as event. Your view listen to this key/id and then get the specific data from your store still using this id/key.
Hope it's clear, let me know.

Categories