Nuxt Maximum call stack size exceeded - javascript

I tried to set the interval for the function to delete it when the component will be destroyed but get this error. And can't find any solution for this.
My interval function:
<script>
export default {
data: () => ({
ordersInterval: null,
}),
async fetch() {
const data = await this.$axios.post(`${this.apiURL}orders`, {
per_page: this.$store.state.pagination.per_page,
page: this.$store.state.pagination.current_page,
})
this.orders = data.data.data
this.$store.dispatch('changePaginationData', {
paginationData: data.data.meta,
})
this.ordersInterval = setInterval(() => {
this.filterOrders()
}, 10000)
},
}
</script>
How can I fix this error?

ESlint is complaining and marking it as an error.
It's probably because fetch() needs to know when the fetching is done at some point, for helpers like $fetchState.pending especially.
I've used a setInterval in some of my code but it's called on an event. You could eventually have a watcher and call the setInterval when it's true (toggling it in your fetch() hook).
If you can, try to use websockets or a system a bit more flexible than polling.
Polling can be tricky to write also (with this), here is a good answer on how to to write it: https://stackoverflow.com/a/43335772/8816585

Related

alternatives to componentDidMount....Axios?

I have recently done a few API tests for a new job. Just receiving data and passing it through. Although I have completed the tasks and it works functionally, the people I walk through it with are not huge fans of componentDidMount.
They do not suggest an alternative? Anyone know why this could be? Is it due to it being async?
The new modern way to do it is: useEffect
First some code (from the docs):
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
At the end-of-the-day, the componentDidMount purpose is to execute something(the side effect) because the component was mounted(the reason or event).
So you can specify array of dependencies (or causes) for re-running like so:
useEffect(() => {
// ....
}, [someVar]);
so if someVar changed, the function will re-run.
Special use cases are; omitting this argument, will cause it to run once, on-mount event. and specify empty array will cause it to run on each re-render.
For the componentWillUnmount:
Just return a function from the inner function like so:
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// Specify how to clean up after this effect:
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});

Fetch Data with interval in javascript?

i have the following code with Pusher:
Echo.private("channel").listen(".foobar", (e) => {
this.fetchData();
});
When there is an incoming request, I want data to be fetched again. But not on every Pusher event. I want the data to be fetched only once in 5 seconds.
I was thinking about a interval that is reset when there is a new incoming event, but how to achieve this?
Ok, first of all it's a Laravel Echo code. It may be using pusher behind the scenes, but saying it's a pusher code is slightly incorrect.
Then, if I understand you right, you need some kind of debounce function. For example lodash offers one.
const debouncedFunction = _.debounce(() => {
this.fetchData()
}, 5000);
Echo.private('channel').listen('.foobar', e => {
debounced()
})
debounce will create debounced version of function which you can then call, or even cancel, because it comes with cancel method.
debouncedFunction.cancel()

vue.js: Where to put regularly repeating background query

I would like to query an API service every 15 seconds, so I can get data from a database and check whether something was changed. If there was a change, then my front end would update automatically because of how vue works.
while (true) {
setTimeout(function() {
QueryService.orders().then(response =>
this.orders = response.data
)
}, 15000)
}
My questions are:
Is this a good approach to solve such a problem at all?
What would be the best position in the code to place such a loop?
EDIT:
Using setInterval() seems to be the right way, but using a polling function with setInterval in the created() hook doesn't affect the data-table at all. It shows me "No data available":
data () {
return {
headers [
{ ... },
{ ... }
],
orders: []
}
created () {
setInterval(function() {
QueryService.orders().then(response => this.orders = response.data)
}, 15000)
}
Using the polling function without setInterval works and fills my data-table with data as usual:
created () {
QueryService.orders().then(response => this.orders = response.data)
}
For a simple and quick solution, I'd go with I'mOnlyVueman's answer. Here some example code I found from Vue.js polling using setINterval(). This example includes
pollData method initiated on created that dispatches a store action (which would call the API)
Canceling the poll as you navigate to another page using beforeDestroy
Code
data () {
return {
polling: null
}
},
methods: {
pollData () {
this.polling = setInterval(() => {
this.$store.dispatch('RETRIEVE_DATA_FROM_BACKEND')
}, 3000)
}
},
beforeDestroy () {
clearInterval(this.polling)
},
created () {
this.pollData()
}
But polling an API isn't very elegant and it doesn't scale well. You'll likely need to do something with Websockets, setting up your app to listen for events pushed from your API.
Here's info on Subscriptions in Vue-Apollo & GraphQL that Denis Tsoi mentioned.
Subscriptions are a GraphQL feature that allows the server to send
data to the clients when a specific event happens on the backend.
Subscriptions are usually implemented with WebSockets, where the
server holds a steady connection to the client. That is, the
Request-Response-Cycle that we used for all previous interactions with
the API is not used for subscriptions. Instead, the client initially
opens up a steady connection to the server by specifying which event
it is interested in. Every time this particular event happens, the
server uses the connection to push the data that’s related to the
event to the client.
A loop like this would go in the component's script within a mounted () lifecycle hook.
That would mean once the component loads your loop would trigger. For detailed guidance on this technique the Vue docs are a good first stop, as well as this article.

How to test a method with setTimeout and jquery using jest

I'm struggling a bit to find a solution on how to test this exported function with jest.
export const scrollToError = () => {
setTimeout(() => {
const hasErrorElement = jQuery('.has-error');
if (!hasErrorElement.length) return;
jQuery('html,body').animate({
scrollTop: hasErrorElement.offset().top - 50,
}, 'slow');
}, 400);
};
I imported it in my test file and tried to start it:
import { scrollToError } from './utils';
describe('Utils', () => {
it('should scroll to error', () => {
const result = scrollToError();
expect(result).toBe(true); //added this just to force an error and got result as undefined
});
});
Could anyone give me any tips on how to test code with these dependencies?
scrollToError() is asynchronous function and you can't invoke it and expect the result to be there immediately. You need to wait that amount of ms (400 in your case), before testing for it.
Asynchronous code is tested a bit differently in Jest: Testing Asynchronous Code. You can also take control over the timers or combine it all with the manual mocks and override jQuery itself.
How are you using jQuery?
I mean, did you get it using npm or yarn? to mock node_modules you can follow this link: https://jestjs.io/docs/en/manual-mocks#mocking-node-modules
Otherwise, you will have to create a manual mock. You can see how to do it here: https://jestjs.io/docs/en/manual-mocks
Updated:
the simplest way is to override it, is while settting up your test at beforeXXX method.
You can simply put something like window.JQuery = jest.fn();
this is the simplest mock ever but you will have to create the methods like animate and other jquery related methods.
Having second thoughts here and looking to your function, if you mock jQuery what else left to be tested?
If you mock, you will be testing if your fn are doing the steps you defined here. Like check if the jQuery fn was called with .has-error class or if animate received the correct parameters.
This kind of test doesn't help you at all, it's just checking if it's following line by line your algorithm. The problem here, that you could do some refactorings like changing the .has-error class name or the animate method by other improved one.
What you really need to change, if it's doing at the end what should be doing. Displaying the div or whatever that should be displayed. If you test that, regardless the way you refactor your code the test will check if the final solution still works and that what matters.
Was I clear? English is not my first language so, it may be a little bit confusing
I finally managed to find a proper solution.
I wrote three test cases for it:
jest.useFakeTimers();
describe('utils', () => {
afterEach(() => {
document.body.innerHTML = '';
});
it('ScrollToError - should run the settimeout for 400 ms', () => {
scrollToError();
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 400);
});
it('ScrollToError - should scroll to error', () => {
document.body.innerHTML = formStep1ErrorMock;
window.setTimeout = fn => fn();
const result = scrollToError();
expect(result).toBe(true);
});
it('ScrollToError - should do nothing as has no errors', () => {
document.body.innerHTML = formStep1Mock;
window.setTimeout = fn => fn();
const result = scrollToError();
expect(result).toBe(true);
});
});
So basically, first I check if the setTimeout was called with the proper amount of seconds (not that it's important).
Then I mock the setTimeout by doing this window.setTimeout = fn => fn(); so it runs without waiting for the delay. I also mock the html with the proper details I need it to have.
And finally, I just cover another scenario.
PS: I added a return true statement to the scrollToError method to make it simpler to have an expected result.
This way I achieved 100% coverage for this method.

setInterval with setState in React

I have a timer using setInterval() in a React component and I'm unsure what the best practices are in order to start and stop this interval in respect to using state. I'm running into some asynchronous issues with that.
Let's say I have a set of links in my React component that render and execute the callback fine:
let links = [10, 50, 100, 500, 1000].map((num) => {
return(
<Link key={num} onClick={(e) => this.switchNums(num)} to={`/somePath/${num}`}>{num}</Link>
)
})
Here's the switchNums() function, where i want it to reset an existing timer:
switchNums(num){
this.stopTimer()
this.reset(num)
}
Here's startTimer(), stopTimer() and reset():
startTimer(){
if(!this.state.timerId){
let timerId = setInterval(()=>{
let timer = this.state.timer + 1
this.setState({
timer: timer,
timerId: timerId
})
}, 1000)
}
}
stopTimer(){
clearInterval(this.state.timerId)
this.setState({timerId:null})
}
reset(size){
this.setState({
gameOver: false,
counter: 0,
correct: 0,
numbers: this.getRandomNumbers(size),
timer: 0
}, this.startTimer())
}
One of the bugs is clicking on the links rapidly will cause multiple intervals to fire despite the if condition in startTimer(). I'm guessing this has to do with the asynchronous nature of setState(). Another bug (and I think related) is that when i click slowly, it only starts the interval every other time.
Can anyone shed some light on this? Or what they've done to circumvent asynchronous issues with setState being used in conjunction with setInterval(any way set state can return a promise?), Or which lifecycle methods would be best for this type of situation?
I think the biggest flaw here is that you're using state to store your interval. While technically possible, I see no reason why you would actually want to do that.
Instead, just use a local variable to your component:
startTimer(){
if(!this.timerId){
this.timerId = setInterval(()=>{
//your function
}, 1000);
}
}
stopTimer(){
clearInterval(this.timerId);
}
So I don't think you need to use the state at all here for your timer. You have some other general questions in your post though that are related to state, and I'll try to answer those below. Just bear in mind that they are irrelevant in solving your particular issue.
What have they've done to circumvent asynchronous issues with setState()?
You can use a callback to execute code after the state has been set. There's a section of the official docs about this; here's what it says:
The second parameter is an optional callback function that will be executed once setState is completed and the component is re-rendered.
setState(nextState, callback);
Which lifecycle methods would be best for this type of situation?
The same section of the doc as above continues:
Generally we recommend using componentDidUpdate() for such logic instead.
If you have multiple setState in your function, and you want to execute specific code after a specific event, I think you're fine using the callback. For more general purposes use the life-cycle method above.
Using React Hooks useState and useEffect you can do the following:
const [timer, setTimer] = useState(1);
useEffect(() => {
const timerId = setInterval(() => setTimer(timer + 1), 1000);
return () => clearInterval(timerId);
});

Categories