How to reset all values to initial state in vue3 - vue.js

In vue2, we can use Object.assign(this.$data, this.$options.data()) to reset all values
But how can we do the same thing (one line coding) in vue3, assuming my setup() is ....
setup(props, context) {
let a = ref('value1')
let b = ref('value2')
let c = ref('value3')
function reset() {
Object.assign(????, ????) or ???? <-- ????
}
}

Object.assign(this.$data, this.$options.data()) is a workaround that relies on options API internals.
It's a good practice to have reusable function for scenarios where initial state needs to be accessed more than once:
const getInitialData = () => ({ a: 1, ... });
It can be called both in data and setup.
Component state or its part can be expressed as single object. In this case it's handled the same way as Vue 2 state:
const formData = reactive(getInitialData());
...
Object.assign(formData, getInitialData());
Otherwise a.value, etc need to be assigned with respective initial values, either manually or with helper function:
let data = getInitialData();
for (let [key, ref] of Object.entries({ a, b, ... }))
ref.value = data[key];

Related

vue and vuex: Evrything works, just want to see how shorten a code. Is there sort of watcher or what's the best way to shorten a code?

Here are actions on how I implemented a cart. Don't really try to get what's going on the main point is a last line of code in each of a functions. Which is
localStorage.setItem('cart', JSON.stringify(state.cart));
those actions call mutations which in there turn changing variable cart, therefore after every change I save new cart in localStorage. Code works, but I don't like duplication. Is there better way doing ? I thought probably watcher on changing a state variable ? But does anybody know if it exists ? And how to use it?
export async function removeItemFromCart({state, getters, commit}, id){
let ind = getters.index(id);
commit('removeFromCart', ind);
localStorage.setItem('cart', JSON.stringify(state.cart)); //here
}
export async function setCnt({state, getters, commit, rootGetters}, { id, cnt }){
let ind = getters.index(id);
if(state.cart[ind].cnt + cnt >= 1 && state.cart[ind].cnt + cnt <= rootGetters['watches/item'](id).cnt)
commit('addCnt', { ind, cnt})
localStorage.setItem('cart', JSON.stringify(state.cart)); //here
}
I think the best way could be creating another action for setting cart in local storage.
export async function removeItemFromCart({state, getters, commit}, id){
let ind = getters.index(id);
commit('removeFromCart', ind);
commit('setCart', state.cart);
}
export async function setCnt({state, getters, commit, rootGetters}, { id, cnt }){
let ind = getters.index(id);
if(state.cart[ind].cnt + cnt >= 1 && state.cart[ind].cnt + cnt <= rootGetters['watches/item'](id).cnt)
commit('addCnt', { ind, cnt})
commit('setCart', state.cart);
}
export function setCart({commit}, cart) {
localStorage.setItem('cart', JSON.stringify(cart));
}

How to mock values of constant in tested function

I have a problem with mocking constant in my test. I have a file with app configuration. There are just keys with some values nothing more.
appConfig.js
//imports
...
export const CASHING_AMOUNT_LIMIT = 50;
export const CASHING_DELETE_AMOUNT = 25;
...
and this is the reducer that I want to test:
reducer.js
import {
CASHING_AMOUNT_LIMIT,
CASHING_DELETE_AMOUNT,
} from '../appConfig';
...
export const reducer = handleActions({
[REQUEST_DATA]: (state, action) => {
if (payload.count >= CASHING_AMOUNT_LIMIT) {
// do something with data if the condition is true
}
return {
...someState,
};
},
...
In my test, I want to change a value for CASHING_AMOUNT_LIMIT and check if reducer returns current store. And I don't know how to mock this variable in reducer.js. Here is my test:
...//imports
const mockValues = {
CASHING_AMOUNT_LIMIT: 10,
CASHING_DELETE_AMOUNT: 5,
};
jest.mock('../../appConfig.js', () => mockValues);
const {
CASHING_AMOUNT_LIMIT,
CASHING_DELETE_AMOUNT,
} = require('../../appConfig.js');
...
it('MY awesome test ', () => {
expect(CASHING_AMOUNT_LIMIT).toBe(10);
expect(CASHING_DELETE_AMOUNT).toBe(5);
// HERE is all ok CASHING_AMOUNT_LIMIT = 10 and the second variable is 5
// tests are OK
// ....
expect(storeWithCache.dispatch(requestFunction({ test: 'XX'})))
.toEqual(myStore);
...
In the end I use dispatch which call my reducer action and function in reducer.js it runs OK... but with old value for CASHING_AMOUNT_LIMIT it is still 50 (as in appConfig.js) and I need to set 10
Can somebody help me with mocking CASHING_AMOUNT_LIMIT in reducer.js?
This part
jest.mock('../../appConfig.js', () => mockValues);
Needs to be outside the describe block, under the imports.
I have a solution we have all mock in one file testenv.js and it is importing globally I put jest.mock('../../appConfig.js') there and it works!

How to update all attributes of a vue 'observer' with another 'observer'

I have two vue 'observer' data a and b with the same key value, and I need to replace the value of b with the value of a. If the value of b contains a complex object, I want this complex object to be the value of a. Deep copy copy replacement. I have some ideas: this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 }), but then someObject becomes unobservable. Do you have any good methods? thank you very much
https://codepen.io/yakiler/project/editor/DadznN#
var app = new Vue({
el: '#demo',
mounted: function () {
},
methods: {
clickMe(){
let pureData = {...this.copyData.v }; // get a pure data by vue Observer data
let f = v => v && JSON.parse(JSON.stringify(v)) || {};
let needUpdateData = this.originData.v;
let returnValue = Object.assign({}, needUpdateData, f(pureData)); // exec replace value
console.log('pureData',pureData);
console.log('needUpdateData',needUpdateData);
console.log('returnvalue',returnValue);
console.log('originData',this.originData);
}
},
data: function() {
return {
originData:{
v:{a:1},b:2
},
copyData:{
v:{a:0},b:0
}
}
}
})
I would persevere with Object.assign(). You should be able to manage the observers without too much difficulty. The easiest solution is to not have someObject at the root of your component. Create a wrapper for someObject, then when the new value is assigned, the observers will automatically be re-created.
If this is not clear, post some code :-)

Can't get data of computed state from store - Vue

I'm learning Vue and have been struggling to get the data from a computed property. I am retrieving comments from the store and them processing through a function called chunkify() however I'm getting the following error.
Despite the comments being computed correctly.
What am I doing wrong here? Any help would be greatly appreciated.
Home.vue
export default {
name: 'Home',
computed: {
comments() {
return this.$store.state.comments
},
},
methods: {
init() {
const comments = this.chunkify(this.comments, 3);
comments[0] = this.chunkify(comments[0], 3);
comments[1] = this.chunkify(comments[1], 3);
comments[2] = this.chunkify(comments[2], 3);
console.log(comments)
},
chunkify(a, n) {
if (n < 2)
return [a];
const len = a.length;
const out = [];
let i = 0;
let size;
if (len % n === 0) {
size = Math.floor(len / n);
while (i < len) {
out.push(a.slice(i, i += size));
}
} else {
while (i < len) {
size = Math.ceil((len - i) / n--);
out.push(a.slice(i, i += size));
}
}
return out;
},
},
mounted() {
this.init()
}
}
Like I wrote in the comments, the OPs problem is that he's accessing a store property that is not available (probably waiting on an AJAX request to come in) when the component is mounted.
Instead of eagerly assuming the data is present when the component is mounted, I suggested that the store property be watched and this.init() called when the propery is loaded.
However, I think this may not be the right approach, since the watch method will be called every time the property changes, which is not semantic for the case of doing prep work on data. I can suggest two solutions that I think are more elegant.
1. Trigger an event when the data is loaded
It's easy to set up a global messaging bus in Vue (see, for example, this post).
Assuming that the property is being loaded in a Vuex action,the flow would be similar to:
{
...
actions: {
async comments() {
try {
await loadComments()
EventBus.trigger("comments:load:success")
} catch (e) {
EventBus.trigger("comments:load:error", e)
}
}
}
...
}
You can gripe a bit about reactivity and events going agains the reactive philosophy. But this may be an example of a case where events are just more semantic.
2. The reactive approach
I try to keep computation outside of my views. Instead of defining chunkify inside your component, you can instead tie that in to your store.
So, say that I have a JavaScrip module called store that exports the Vuex store. I would define chunkify as a named function in that module
function chunkify (a, n) {
...
}
(This can be defined at the bottom of the JS module, for readability, thanks to function hoisting.)
Then, in your store definition,
const store = new Vuex.Store({
state: { ... },
...
getters: {
chunkedComments (state) {
return function (chunks) {
if (state.comments)
return chunkify(state.comments, chunks);
return state.comments
}
}
}
...
})
In your component, the computed prop would now be
computed: {
comments() {
return this.$store.getters.chunkedComments(3);
},
}
Then the update cascase will flow from the getter, which will update when comments are retrieved, which will update the component's computed prop, which will update the ui.
Use getters, merge chuckify and init function inside the getter.And for computed comment function will return this.$store.getters.YOURFUNC (merge of chuckify and init function). do not add anything inside mounted.

Watch all properties of a reactive data in Vue.js

I had an API call to the backend and based on the returned data, I set the reactive data dynamically:
let data = {
quantity: [],
tickets: []
}
api.default.fetch()
.then(function (tickets) {
data.tickets = tickets
tickets.forEach(ticket => {
data.quantity[ticket.id] = 0
})
})
Based on this flow, how can I set watcher for all reactive elements in quantity array dynamically as well?
You can create a computed property, where you can stringify the quantity array, and then set a watcher on this computed property. Code will look something like following:
computed: {
quantityString: function () {
return JSON.stringify(this.quantity)
}
}
watch: {
// whenever question changes, this function will run
quantityString: function (newQuantity) {
var newQuantity = JSON.parse(newQuantity)
//Your relevant code
}
}
Using the [] operator to change a value in an array won't let vue detect the change, use splice instead.