Debouncing a Vue Component method with Lodash - vue.js

I am attempting to use Lodash's debounce on a Vue 2 method in order to only run once a user has stopped typing in an input field, but I am getting unexpected results:
INPUT FIELD
<input type="text" v-model='filter.user' placeholder="search" #keyup='dTest'>
METHOD
dTest() {
const d = _.debounce(() => {
console.log('hi');
}, 2000);
d();
}
However, 'hi' is being logged to the console on every keypress, with a two second delay.
thanks

Change dTest to:
dTest = _.debounce(() => {
console.log('hi');
}, 2000);
With your dTest, you are creating a new debounce function every time dTest is run. You are meant to create this function only once, like you see above, then call that function every time.

Related

Vue - execute method on blur when no input focused

I'm trying to find a way to prevent method execution when any of 2 inputs have focus.
Both inputs should fire method on blur and prevent it when focused. Using single of these inputs works good.
The problematic situation is when blurring from input_1 and immediately focusing on input_2 (e.g. using tab) - it fires the method before input_2 gets focus.
What I would like to achieve is preventing the method execution in this case.
What I already tried:
using flags as presented in example code -> failed as described,
getting activeElement in fired method -> instead of getting input element, I'm getting main vue app <div id="app" />
Example code:
<template>
<div>
<input id="input_1" #blur="changeFlagAndExecuteMethod()" #focus="focused = true"></input>
<input id="input_2" #blur="changeFlagAndExecuteMethod()" #focus="focused = true"></input>
</div>
</template>
<script>
export default {
data () {
return {
focused: false
}
},
methods: {
changeFlagAndExecuteMethod () {
this.focused = false
this.doSomething()
},
doSomething () {
// document.activeElement => results with main <div id="app" /> instead of input
if (this.focused) return
// otherwise execute method
}
}
}
</script>
One solution is to execute doSomething() in the next macro tick, using setTimeout() with a zero delay (ms is 0 by default), at which point the second input will have received focus, and its focus handler will already have been invoked:
changeFlagAndExecuteMethod () {
this.focused = false
setTimeout(() => this.doSomething()) // execute in next macro tick
}
demo

You may have an infinite update loop in a component render function error - solution?

I am getting error "You may have an infinite update loop in a component render function." What should I do?
I have tried making the arrays a data value. Also, I have tried using a for loop. It seems like it's isolated in the first method.
data() {
return {
activeTab: 0,
uniqueLobs: []
}
},
methods: {
addDollarSymbol(val){
var newVal = "$" + val;
return newVal.replace(/<(?:.|\n)*?>/gm, ''); // Trims white space
},
removeDuplicateLOB(lineOfBusiness) {
// Removes duplicate LOBs for tabs
let incomingLobs = [];
lineOfBusiness.forEach((business) => {
incomingLobs.push(business.line_of_business.name);
});
this.uniqueLobs = [...new Set(incomingLobs)];
return this.uniqueLobs;
},
showSpecificLobData(activeTab){
//compares tab LOB to all incoming card data and shows only the LOB data for that specific tab
let activeTabData = [];
this.product_rate_card.forEach((product) => {
if (product.line_of_business.name == this.uniqueLobs[activeTab] ) {
activeTabData.push(product);
}
});
return activeTabData;
}
}
The 'loop' in this case refers to an infinite recursion rather than a for loop.
That warning is logged here:
https://github.com/vuejs/vue/blob/ff911c9ffef16c591b25df05cb2322ee737d13e0/src/core/observer/scheduler.js#L104
It may not be immediately obvious what most of that is doing but the key part of the code is the line if (circular[id] > MAX_UPDATE_COUNT) {, which is checking whether a particular watcher has been triggered more than 100 times.
When reactive data changes it will cause any components that depend on that data to be re-rendered. If the rendering process changes that same data then rendering will be triggered again. If the data never stabilizes then this will continue forever.
Here's a simple example of a component that triggers that warning:
<template>
<div>
{{ getNextCount() }}
</div>
</template>
<script>
export default {
data () {
return {
count: 1
}
},
methods: {
getNextCount () {
this.count++
return this.count
}
}
}
</script>
The template has a dependency on count but by calling getNextCount it will also change the value of count. When that value changes the component will be re-added to the rendering queue because a dependency has changed. It can never break out of this cycle because the value keeps changing.
I can't say for sure what is causing this problem in your component as you haven't posted enough code. However, it could be something like the line this.uniqueLobs = ..., assuming that is being called during rendering. In general I would suggest avoiding changing anything on this during the rendering phase. Rendering should be read-only. Generally you'd use computed properties for any derived data that you want to keep around.
Most times it’s as a result of how you're passing props to another component.
If it’s Vue.js 2, try using v-on:[variable-name].

Vue js stop second event

I have input with some info.
On blur event or on enter press I want to do some action
But when I press enter my input loses focus and two events are fired one after another-what do I do?
<input v-on:keyup.13.prevent ='save_renamed_group(g)'
#blur.prevent = 'save_renamed_group(g)'>
UPD: I don't consider my question as duplicate of this one:
Prevent both blur and keyup events to fire after pressing enter in a textbox
simply because I want a clear and clean and nice solution to this simple and common stuff and all solutions posted there look like a little bit of hell.
Solution 1: apply debounce on the method.
Debouncing essentially groups your events together and keeps them from
being fired too often. To use it in a Vue component, just wrap the
function you want to call in lodash’s _.debounce function.
https://alligator.io/vuejs/lodash-throttle-debounce/
import { debounce } from 'lodash';
export default {
methods: {
// group all function calls within 100ms together
// no matter how many times this function is called within 100ms, only 1 of them will be executed.
save_renamed_group: debounce(g => {
// ...
}, 100),
},
};
Pros: simple
Cons: delayed function execution
Solution 2: store state of function execution in a variable
export default {
created() {
// create the variable
this.save_renamed_group_running = false;
},
methods: {
save_renamed_group(g) {
// exit if function is already running
if (this.save_renamed_group_running) return;
// set running to true
this.save_renamed_group_running = true;
// .... other logic ...
// set running to false before exiting
this.save_renamed_group_running = false;
return /* something */;
},
},
};
Pros: immediate function execution
Cons: verbose

Not understanding behavior of MobX-utils fromResource

This is a modified version of the now() implementation from mobx-utils. From my understanding, when the autorun function is triggered, "initial" would be logged, then after 1 second, the value of Date.now(), then Date.now() again and again every second.
function createIntervalTicker(interval) {
let subscriptionHandle
return fromResource(
sink => {
subscriptionHandle = setInterval(
() => sink(Date.now()),
interval
);
},
() => {
clearInterval(subscriptionHandle);
},
'initial'
);
}
autorun(() => {
console.log(createIntervalTicker(1000).current())
})
However, I am getting "initial" logged out every second again and again. The value of Date.now() is never logged.
It seems that when sink(Date.now()) is called, it is only triggering the the autorun function, but not updating the value returned by current().
I would appreciate any advice. Thanks in advance.
mobx#3.1.16
mobx-utils#2.0.2
Your code creates a new observable and passes its value to console.log each time autorun is executed. Hence, you always see initial in the browser console: mobx tracks changes to the initial observable, but console.log receives new observable on each reaction.
Solution: store a reference to the initial observable and reuse it
const ticker = createIntervalTicker(1000);
autorun(() => {
console.log(ticker.current())
})

Why would 'this.data' go out of scope?

I've got a button that calls a procedure onApproximateAgePress which fails to see 'this.data'.
<Button title="<20" onPress={onApproximateAgePress}/>
onApproximateAgePress () {
console.log('onApproximateAgePress') ; // outputs onApproximateAgePress
console.log(this.data) ; // outputs null
}
// Getter for "Immutable.js" state data...
get data() {
return this.state.data;
}
// Setter for "Immutable.js" state data...
set data(data) {
this.setState({ data });
}
state = {
data: fromJS({
showApproximateAgePicker: false,
...
}),
}
I have other components that have been wired up in a similar way but they do see this.data.
If I change the call to
<Button title="<20" onPress={(e)=>console.log(this.data) }/>
I get a log of with valid values.
Why would 'this.data' go out of scope?
Well in both the alternatives , there is one major difference , on the first approach you are just assigning a callback to the onPress Event , where as in second you are using arrow function.
So what does arrow function do? for starter it binds the lexical scope of 'this' ,in second case to the block where you are logging this.data
Read for More : Arrow Functions
so this you are referring in onApproximateAgePress() does not equal to current instance of this component, you should bind it.
or
Solution I : <Button title="<20" onPress={this.onApproximateAgePress.bind(this)}/>
Solution II : <Button title="<20" onPress={() => onApproximateAgePress()}/>
You should be able to access this and hence this.data