I am trying to build a watchlist (data streaming program) by vue3 with vuex. When a watchlist component subscribes for a symbol it should receive updates for that symbol from the store. When removing the subscription from the component that particular component should not receive that state change after that. We cannot hardcode symbol names in store to mapstate from component for each individually since there can be hundreds. if we take all the symbols as an attribute of a single object and map the state to it it will be a performance overhead since not all watchLists are referring to all the symbols.
So my question is there any way to inject a dynamically changing array to mapstate?
In component->
computed: {
...mapState([this should change dynamically]),
},
in Store->
state : {
these states also should be dynamic
},
or is there any workaround in vue to achive this?
I have found an alternative way by using getters. When we are using that we don't have to map state at all.
I am returning a function from getters with an argument since Vuex getters don't accept arguments.
symbol: (state) => (symbol) => {
return state.payload[symbol]
}
and from the component, I am watching for that getter.
this.$store.watch(
(state, getters) => getters.allSymbols(currency),
(newValue, oldValue) => {
this.symbolObjects[symbol] = newValue;
// do something
},
);
the watch is triggering every time the currency update. Also using the Vuex watcher gives the benefit of accessing both old and new values.
Related
I have this same design as an official nested vuedraggable example here.
I am using vuex in my version and I am wondering if it is possible to update the items within the rows with a getter/setter computed property so they trigger updating the store state. I have essentially the same file but the computed property for the getter and setter for rows updates the state, however, I can't use a getter and setter on the subcomponent (items) as I don't believe I can pass a parameter to the computed property to identify the row it resides in.
How could I do this with vuex and having an array structure of rows that have arrays of items within each row?
UPDATE
#HuyDuy I took your advice. I was able to use a function with an object including multiple pieces of data in it. In case someone finds this useful in the future this is how I got it to work:
methods: {
...mapActions('builder', ['setSteps']),
},
computed: {
...mapGetters('builder', ['getSteps']),
steps: {
get() {
return this.getSteps(this.stageID);
},
set(val) {
this.setSteps({steps: val, stageID: this.stageID});
}
},
},
In vuex then I have a getter like this:
getSteps: (state) => (stageID) => {
return state.stages.find(stage => stage.id === stageID).steps;
},
And a setter (mutation) like this, (an action not listed here just commits it):
SETSTEPS(state, payload) {
state.stages.find(stage => stage.id === payload.stageID).steps = payload.steps;
},
This seems to sync the data, is reactive and because it's handled by a mutation I can track changes, (to provide undo and redo), which is what I was after.
I am learning Vuex and my understanding so far has been that mutations should be simple functions that update the state directly using something like state.property = value or state.object = {payload}, e.g.:
SET_USER_DATA (state, userData) {
state.user = userData
}
I am working through a course from Vue Mastery that contains mutations that look like the code below which does not reference the state object within the body of the mutation at all:
CREATE_TASK(state, { tasks, name }) {
tasks.push({ name, id: generateId(), description: "" });
},
UPDATE_TASK(state, { task, field, value }) {
Vue.set(task, field, value);
},
CREATE_TASK creates a new empty task and adds it to the tasks array using tasks.push(), but shouldn't a reference to the state object be required to update the state? E.g. state.tasks.push()? How does simply pushing an item onto a bare array commit the change to the state?
In the second example, they use Vue.set() to update the value of a specific field within a task (e.g. name, description), but again, there is no reference to the state object here.
The best I can figure is that they relying on Vue's native reactivity to automatically update the state when calling Vue.set() or Array.push(). But if that is the case, wouldn't any usage of Vue.set() or Array.push() inside of a component also immediately update the state (violating the rule that state changes should only be handled within a mutation?
In vuex, I have this:
getByLessonId: state => _lessonId => {
return state.entities.filter(e => e.lesson.id === _lessonId);
},
In component:
// using as a method...
...mapGetters("assignment", { getAssignmentsByLessonId: "getByLessonId" }),
Later in the code, since the mapping returns the function, I need to call it like this?
// load this lessons assignments...
this.assignments = this.getAssignmentsByLessonId()(this.id);
// this is what i started with
// this.$store.getters["assignment/getByLessonId"](this.id)
It works, just not sure if there is a better way to do this? Or should I put mapGetters in the computed properties of the component?
Better put mapGetters (as long as mapState) in a computed section of a component. This is a recommended way to use several getters or props of a state because you have one place only to control getters and props in a component.
I’m using vuex with a tree of data. It's reactive and works well because we have a corresponding tree of components. Since the structure is a tree, it’s common to want to mutate a deeply nested child object. The easy way to implement that is with a mutation that accepts the child in its payload:
removeLeaf(state, { child, leaf }) {
child.children = child.children.filter((i: any) => i !== leaf);
state = state; // silence warning
},
A different way would be to change the mutation to work like this:
removeLeaf(state, { child_, leaf }) {
let child = searchForChild(state, child_);
child.children = child.children.filter((i: any) => i !== leaf);
},
I’m happy with the first way. Are there any drawbacks to writing mutations that modify a child object of state by using payload instead of the state parameter?
I don't think it'll work properly.
Vue's and Vuex's reactivity system is based on Javascript Getter and Setter.
If you console.log the state in any mutation, you will see something like this:
The get and set is the getter and setter of your Vuex States.
Without the setter, Vue's reactivity system likely wouldn't work.
Try console.log state and child in your mutation. You will likely see that the child doesn't contain setter.
If the setter is not there, Vue wouldn't know that you have updated the state of child, and you will likely have reactivity problem.
Let's say the vuex state has a structure like this:
state: {
houses: [{
pk: 1,
historicalName: 'Poo'
}],
},
And house instances from the state are used by passing it as props to components (houses.vue template):
<div><house v-for="hs in housesState" :house="hs"></house></div>
Then when I need to mutate fields of house, do I write mutation like this:
[Types.mutations.SET_HISTORICAL_NAME]: (state, payload) => {
state.houses.find(x => x.pk === payload.house.pk).historicalName = payload.historicalName
}
or do I pass the instance of the house from component
this.$store.commit(Types.mutations.SET_HISTORICAL_NAME, {house: this.house, historicalName: 'Gold'})
and update it without searching the array?
[Types.mutations.SET_HISTORICAL_NAME]: (state, payload) => {
payload.house.historicalName = payload.historicalName
}
Will the second option cause any locks in the state? Mutation could be called simultaneously for different houses. Will it be faster? Not searching the array every time should help but I'm not sure it's that straightforward here.
House data is also used by another components (I've simplified data structure in the question) so I don't think I can only store house on local component level.