How to make a debounce function act independently on different instances of the same component on Vue.js? - vue.js

I have two Vue components that have a "save to disk" call on every change of data, it is loaded into these components via a mixin and each component save into a different file, so they must function independently (only trigger a debounce reset on its own change of data). To prevent too much writing to the disk. Here's my debound function:
function debounce(fn, delay) {
var timeoutID = null;
return function () {
console.log("clearing " + timeoutID)
clearTimeout(timeoutID);
var args = arguments;
var that = this;
timeoutID = setTimeout(() => fn.apply(that, args), delay);
};
}
Here's the methods on my mixin that the components inherit:
methods: {
saveData: debounce(function(){
console.log('saving widget: ' + this.$parent.widget.id);
this.saver.store = this.persisted;
}, 5000),
},
It works well when I'm changing data on one or the other component, but when I change data on one and before debounce ends I change on the other, it cancels my debounce function from the first one, and only saves the second component data.
What am I missing here?

I've encountered the same issue today. I've tried something else that is, in my opinion, a bit cleaner. Instead of defining the debounced function within the methods block like you did, try defining it as part of the data like so:
data() {
return {
saveData: debounce(function(){
console.log('saving widget: ' + this.$parent.widget.id);
this.saver.store = this.persisted;
}, 5000)
}
}
You can call the method the same way as you would normally. From the docs: "A component’s data option must be a function, so that each instance can maintain an independent copy of the returned data object." This way, each instance that uses the debounce function will have it's own unique instance of it.
More on how this works can be found here: https://v2.vuejs.org/v2/guide/components.html#data-Must-Be-a-Function

That's because each component instance shares the same debounced function, only the context (this, that) is varying.
A simple workaround would be to change your debounce implementation to
function debounce(fn, delay) {
var thatUidToTimeoutID = {};
return function () {
var args = arguments;
var that = this;
clearTimeout(thatUidToTimeoutID[that._uid]);
thatUidToTimeoutID[that._uid] = setTimeout(() => fn.apply(that, args), delay);
};
}
_uid is holding an unique id of each component, it's more of an internal property (hence it's weird key) but it should be good enough.

Related

Vue composite make function reactive?

Is it possible to create a composite function such that consumer can provide a reference to a function? In mixins you could just call the consumer method as long as it existed. Here is how I would think this would work but it doesn't. method1 is not reactive. I realize this might be an antipattern. Sometimes you run an async/ajax call and once it's done it would be nice to just run the callback method1 instead of changing a dummy value and running a watch on it in the consumer. Ideas?
-- consuming component
setup() {
let { prop1, method1, method2 } = useComposite();
method1 = (o) =>
{
console.log(o);
};
method2();
}
composable function --
export default function useComposite()
{
let method1 = ref(()=>{});
const method2 = () =>
{
method1.value("hello");
};
const prop1 = ref(o);
return {
method1,
methoid2,
prop1,
};
}
method1 is already a ref and used as such. If it's redefined, this should be done like:
method1.value = (o) => ...
This is definitely an antipattern. There could be multiple callbacks, while it's limited to one. A more complete solution could use event bus like mitt and register handlers for an event.
A proper solution with composition API would involve a watcher. Nothing requires composition API here. It's just callback-based control flow and a step back from promise-based one. If method2 is asynchronous, it needs to return a promise and be chained in a place it's used:
const method1 = (o) => ...
method2().then(method1);

RN with Firestore unable to wait for Promise to resolve

I have a simple call to Firestore to write a doc and then wait for the doc to finish writing before changing state of the parent. However, the parent state is being changed too fast, resulting in reading fields that I think have not yet been written/propagated. I tried adding a delay with setTimeout and it seems ignored. How can I make sure the state change is absolutely only called after the Firestore doc is written completely?
The code:
updateDBEntry(stateObj) {
var that = this;
var docRef = firebase.firestore().collection('sessions').doc(this.state.userID);
docRef.get().then((doc) => {
if (!doc.exists) {
const timestamp = firebase.firestore.FieldValue.serverTimestamp();
var duration = (stateObj.seshDuration) ? stateObj.seshDuration : 1;
docRef.set({
seshName: stateObj.seshName,
seshStreet: stateObj.seshStreet,
seshZipcode: stateObj.seshZipcode,
seshDuration: duration,
seshDesc: stateObj.seshDesc,
seshTime: timestamp,
}).then(() => {
var handleToUpdate = that.props.handleToUpdate;
setTimeout(() => {
handleToUpdate(1); //this changes the parent's state
}, 10000);
});
}
});
}
I'm not sure exactly the problem you're running into here, mostly because you've only shown this one function, and not how you're using it in the rest of your app. But I can tell you three things for sure:
When the promise from set() resolves successfully, you can be certain the document is written.
get() and set() are asynchronous, and so is then(). They all return promises the represent async work.
Item 2 means that your entire function updateDBEntry() is also asynchronous and returns immediately, before any of the work is complete.
Because this entire function is async, when it returns, the document will not have been created yet. Instead, maybe this function should return that resolves only after all the work is complete, so that the caller can also use it set up some code to execute after the work is done.

What is the difference between returning an action vs returning the whole function in Page Object?

What is the difference between returning an action vs returning the whole function in Page Object?
this.download = function() {
element(by.id('modal-download-button')).click();
return this;
};
VS
this.download = function() {
return element(by.id('modal-download-button')).click();
};
Sometimes, to tackle timing and syncing issues, you want to explicitly resolve a promise returned by click(). In this case returning the "click" promise makes sense:
pageObject.download().then(function () {
// ...
});
Returning a full page object could be useful for chaining page object methods:
pageObject.download().get().verify();

How to override dojo's domReady

I want to override dijit._CssStateMixin's domReady() method.
Is there any way to override that instead of changing the listener mechanism in Dojo.
I tried overriding _cssMouseEvent() method in simple javascript, but it still does invoke dijit's _cssMouseEvent() from domReady().
I have tried following approach:
dojoConfig = {
map: {
'dijit/_CssStateMixin': {
'dojo/domReady': 'app/noop'
}
}
};
I have added 'app' folder and then 'noop.js' inside that.
noop.js has nothing in it:
define([], function () {
return function () {};
});
Even after this I can see that dijit.js's _CssStateMaxin domReady() getting called from listener.apply (code snippet pasted below)
var addStopImmediate = function(listener){
return function(event){
if(!event.immediatelyStopped){// check to make sure it hasn't been stopped immediately
event.stopImmediatePropagation = stopImmediatePropagation;
return listener.apply(this, arguments);
}
};
}
If your ultimate goal is to prevent the domReady callback in dijit/_CssStateMixin from running, your simplest bet is likely to re-map dojo/domReady to a different module that doesn't call the callback at all, when loaded via dijit/_CssStateMixin.
NOTE: Stripping out these handlers might have adverse visual effects on Dijit widgets which inherit _CssStateMixin, since it may hinder the application of Dijit CSS classes related to hover and focus. But if your concern is that _CssStateMixin is hampering performance, it may at least be worth a try to confirm or deny your suspicion.
First we have to create a simple module that returns a function that does nothing, which we will later substitute for dojo/domReady when loaded by dijit/_CssStateMixin, so that it can still call domReady but it won't execute the callback it passes.
For simplicity's sake I'll assume you already have a custom package that you can easily add a module to; for this example I'll assume it's called app. Let's create app/noop:
define([], function () {
return function () {};
});
Now let's configure the loader to map app/noop in place of dojo/domReady specifically when loaded by dijit/_CssStateMixin:
var dojoConfig = {
...,
map: {
'dijit/_CssStateMixin': {
'dojo/domReady': 'app/noop'
}
},
...
};
Now the offending domReady callback should no longer be run.
If you're curious about map, you can read more about it in this SitePen FAQ.

Dojo 1.7 how to use dojo components outside of require()

I have created Dojo widget like below using AMD loader in Dojo 1.7.2
var myCpane;
require([
"dijit/layout/ContentPane"
], function(ContentPane) {
myCpane = new ContentPane();
});
myCpane.startup(); // It gives 'myCpane' as undefined
In the above example, in the last statment, the variable 'myCpane' is coming as 'undefined', if I use the 'myCpane.startup()' inside the 'require()' callback function then, it will work fine.
But I want to use that 'myCpane' variable on outside of the 'require' function (for many reasons). I know the 'require()' callback function execution delayed due to the component loading process by Dojo.
My question is,
How to block the 'require()' function until it completes to execute it's callback function.
So the variable 'myCpane' will not be 'undefined' when the control come out from the 'require()' function
===========================================================
To overcome this issue, I have written a small function to load the modules and wait until the module load complete
LoadModule: function(modulePath) { // modulePath = "dijit/layout/ContentPane"
var moduleObject = undefined;
require({async: false}, [modulePath], function(getModuleObject) {
moduleObject = getModuleObject;
});
// Wait until the module loads completes
while(moduleObject === undefined);
// Return the loaded module.
return moduleObject;
}
The output of the function is always executing the while loop, the control never comes inside of 'require()'s callback function to set the value to the variable "moduleObject".
When the 'require()' function will call it's callback function? I have verified using the browser debugger window the file 'ContentPane.js' is loaded properly, but the callback function is not called, If I comment the while loop then, the callback is called properly.
When the control will come inside of the callback function in my case ?
I'm not sure what are you about to achieve, but it looks for me like a programming anti-pattern. Anyway you can achieve this via dojo/_base/Deferred:
require(["dojo/_base/Deferred"], function(Deferred) {
var deferred = new Deferred();
require(["dijit/layout/ContentPane"], function(ContentPane) {
var myCpane = new ContentPane();
deferred.resolve(myCpane); //resolve, i.e. call `then` callback
});
deferred.then(function(myCpane) {
console.log(myCpane);
myCpane.startup();
});
});​
Mess with it at jsFiddle: http://jsfiddle.net/phusick/HYQEd/
I would also suggest you consider one of these two strategies to achieve the same:
Give the ContentPane an id and obtain its reference via dijit's registry.byId().
Create ContentPane instance in a separate module and expose it as a return value of that module:
// file: myCpane.js
define(["dijit/layout/ContentPane"], function(ContentPane) {
var myCpane = new ContentPane();
return myCpane;
});
// file: main.js
require(["./myCpane"], function(myCpane) {
myCpane.startup();
});
I think this goes more to scope issue then amd loader question; consider
var x;
function foo() {
x = { bar : 1 };
}
// you wouldn't expect to have reference to x variable here
if(typeof x.bar == "undefined") console.log(x);
// foo() is called at a random time - or in dojo loader case, when modules are present
foo();
console.log(x.bar); // oohh now its there ^^
x in this case translates to your myCpane, which is declared as variable (var $$) inside a function, the function that is callback for when loader is done requireing modules.
The Deferred is a nice handler for this as stated below. A slight overhead though, if youre allready in a detached (async) function flow. For full control, look into require() you could do this as well:
var myCpane;
require({ async: false }, [
"dijit/layout/ContentPane"
], function(ContentPane) {
myCpane = new ContentPane();
});
// require does not return until module loading is done and callback executed
myCpane.startup();