Encountered a Vue.js Vuex error: "Unable to dispatch function in context."

I have implemented a stopwatch function that can run the stopwatch as shown below :

Stopwatch.vue

<script>
export default {
  data() {
    return {
      time: "00:00.000",
      timeBegan: null,
      timeStopped: null,
      stoppedDuration: 0,
      started: null,
      running: false,
    };
  },
  methods: {
    start() {
      if (this.running) return;

      if (this.timeBegan === null) {
        this.timeBegan = new Date();
      }

      if (this.timeStopped !== null) {
        this.stoppedDuration += new Date() - this.timeStopped;
      }

      this.started = setInterval(this.clockRunning, 10);
      this.running = true;
    },
    clockRunning() {
      let currentTime = new Date();
      let timeElapsed = new Date(
        currentTime - this.timeBegan - this.stoppedDuration
      );
      let min = timeElapsed.getUTCMinutes();
      let sec = timeElapsed.getUTCSeconds();
      let ms = timeElapsed.getUTCMilliseconds();

      this.time =
        this.zeroPrefix(min, 2) +
        ":" +
        this.zeroPrefix(sec, 2) +
        "." +
        this.zeroPrefix(ms, 3);
    },
    zeroPrefix(num, digit) {
      let zero = "";
      for (let i = 0; i < digit; i++) {
        zero += "0";
      }
      return (zero + num).slice(-digit);
    },
  },
  mounted() {
    this.start();
  }
};
</script>

The above function enables the stopwatch to work properly, but I am looking to refactor it using Vuex so that I can easily access the stopwatch functionality from any component.

index.js - Within Vuex/store/stopwatch

export default {
  state: {
        time: "00:00.000",
        timeBegan: null,
        timeStopped: null,
        stoppedDuration: 0,
        started: null,
        running: false,
  },
  actions: {
    start(context) {
      if (context.state.running) return;
      if (context.state.timeBegan === null) {
        context.state.timeBegan = new Date();
      }

      if (context.state.timeStopped !== null) {
        context.state.stoppedDuration += new Date() - context.state.timeStopped;
            }
      context.state.started = setInterval(() => context.dispatch('clockRunning'), 10);
      context.state.running = true;
    },
    clockRunning(context) {
      let currentTime = new Date();
      let timeElapsed = new Date(
        currentTime - context.state.timeBegan - context.state.stoppedDuration
      );
      let min = timeElapsed.getUTCMinutes();
      let sec = timeElapsed.getUTCSeconds();
      let ms = timeElapsed.getUTCMilliseconds();

      context.state.time =
        context.dispatch('zeroPrefix')(min, 2) +
        ":" +
        context.dispatch('zeroPrefix')(sec, 2) +
        "." +
        context.dispatch('zeroPrefix')(ms, 3);
    },
    zeroPrefix(num, digit) {
      let zero = "";
      for (let i = 0; i < digit; i++) {
        zero += "0";
      }
      return (zero + num).slice(-digit);
    }
    },
  mutations: {},
  getters: {}
}

Upon running the code above, an error has been encountered :

https://i.sstatic.net/nFofo.png

The error seems to be related to this line of code :

context.state.started = setInterval(() => context.dispatch('clockRunning'), 10);

However, there doesn't appear to be any discernible error in the given line.

This is the same line of code that worked prior to transitioning to Vuex :

this.started = setInterval(this.clockRunning, 10);

How can this particular error be resolved?

Update :

A demo code has been created on codesandbox

An attempt was made to implement @Estus Flask's suggestion, yet the error persists. The change was made to the code as follows :

setInterval(() => context.dispatch('clockRunning'), 10)

However, this modification results in a rapidly growing error page, eventually reaching hundreds within a brief period. This error causes my PC to slow down and utilizes 100% CPU performance.

The error presents itself as depicted below :

https://i.sstatic.net/8mO0x.png

Any assistance in resolving this issue would be greatly appreciated.

Answer №1

If the timer hits 90 seconds (1 minute and 30 seconds), I added a stopper, but you have the option to remove it. Just trigger a commit on 'stop'.

 export default {
  state: {
    time: "00:00.000",
    timeStarted: null,
    timeBegan: null,
    timeStopped: null,
    stoppedDuration: 0,
    started: null,
    running: false,
    maxSeconds: 90,
    temp:{
      min: "0",
      sec: "0",
      ms: "0",
      secondsPassed: 0
    }
  },
  actions: {
    start({state, commit, dispatch}) {
      if (state.running) return;
      if (state.timeBegan === null) {
        state.timeBegan = new Date();
      }

      if (state.timeStopped !== null) {
        state.stoppedDuration += new Date() - state.timeStopped;
      }

      commit("start", {
        callback:()=>{
          dispatch("stopIfReachedMaximumSeconds");
          dispatch("clockRunning");
        }
      });
    },
    async clockRunning({state, commit, dispatch}) {
      let currentTime = new Date();
      let timeElapsed = new Date(
        currentTime - state.timeBegan - state.stoppedDuration
      );
      let min = timeElapsed.getUTCMinutes();
      let sec = timeElapsed.getUTCSeconds();
      let ms = timeElapsed.getUTCMilliseconds();

      commit("newTemp", {
        key: 'secondsPassed',
        value: parseInt(Math.abs((state.timeStarted - new Date())/1000))
      });

      if (state.running) {
        await dispatch("zeroPrefix",{num: min, digit:2}).then(zeroPrefixResponse => {
          commit("newTemp", {
            key: 'min',
            value: zeroPrefixResponse
          })
        });

        await dispatch("zeroPrefix",{num: sec, digit:2}).then(zeroPrefixResponse => {
          commit("newTemp", {
            key: 'sec',
            value: zeroPrefixResponse
          })
        });

        await dispatch("zeroPrefix",{num: ms, digit:3}).then(zeroPrefixResponse => {
          commit("newTemp", {
            key: 'ms',
            value: zeroPrefixResponse
          })
        });
        state.time = state.temp.min + ":" + state.temp.sec + "." + state.temp.ms;
      }
    },
    zeroPrefix(context, payload) {
      return new Promise(resolve => {
        let zero = "";
        for (let i = 0; i < payload.digit; i++) {
          zero += "0";
        }
        resolve((zero + payload.num).slice(-payload.digit));
      });
    },

    stopIfReachedMaximumSeconds({state, commit}){
      if(state.temp.secondsPassed >= state.maxSeconds){
        console.log("REACHED MAXIMUM SECONDS");
        commit("stop");
      }
    }
  },
  mutations: {
    newTemp(state, payload){
      state.temp[payload.key] = payload.value;
    },
    addSecondPassed(state, second){
      state.temp.secondsPassed += second;
    },
    resetSecondPassed(state){
      state.temp.secondsPassed = 0;
    },
    start(state, payload){
      state.timeStarted = new Date();
      state.started = setInterval(()=>{
        payload.callback();
      }, 10);
      state.running = true;
    },
    stop(state){
      clearInterval(state.started);
      state.timeStarted = null;
      state.started = null;
      state.running = false;
      state.temp.secondsPassed = 0;
    }
  },
  getters: {}
};

Answer №2

The issue at hand is that the context.dispatch('clockRunning') expression is being treated as a promise instead of a function, which conflicts with the requirement for the first argument of setInterval to be a function.

In order to properly dispatch an action on a time interval, the correct syntax should be:

setInterval(() => context.dispatch('clockRunning'), 10)

It's important to note that the dispatch method returns a promise, not a function. When passing action parameters using dispatch, it should be used in the following way:

context.dispatch('zeroPrefix', min, 2) 

Using the result of the zeroPrefix action directly in an expression is incorrect. Actions are meant to operate on the store and return a promise for asynchronous operations, rather than a direct result. To resolve this, the zeroPrefix action should not be stored within the state but extracted into a separate helper function.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issue: encountered an ECONNRESET error when attempting to read using the request module in mode.js

When attempting to download and parse large XML files from affiliate sites without a proper API, I encounter the same error consistently while using the request module for Node.js. Error: read ECONNRESET at exports._errnoException (util.js:746:11) at TCP. ...

Utilizing hover effects and timeouts to conditionally show React components

I encountered a challenging React layout dilemma. It's not a complex issue, but rather difficult to articulate, so I made an effort to be as clear as possible. The data I have maps individual components in the following way: map => <TableRow na ...

Exploring the Differences: Nuxt Plugins Registration through Global Invocation versus Manual Import

My approach involves creating two repositories as plugins to handle API calls. plugins/repos/UserRepository.js (required in /views/users.vue) plugins/repos/PostRepository.js (needed for /views/posts.vue) There are two options for utilizing these plugins ...

Executing a Jquery click event after a delay with setTimeout

I am working with an <a> element that, when clicked, triggers a handler like this: function onCummReportClick(e) { if ($(e.currentTarget).attr('href').indexOf('csv') !== -1) { { return true; } //Here s ...

Tips for uploading images, like photos, to an iOS application using Appium

I am a beginner in the world of appium automation. Currently, I am attempting to automate an iOS native app using the following stack: appium-webdriverio-javascript-jasmine. Here is some information about my environment: Appium Desktop APP version (or ...

Insert multiple text box values as a new entry in an SQL database

Currently, I am implementing a code snippet to incorporate additional text boxes within a form. The main purpose is to enable users to input multiple languages they are proficient in. <script> jQuery(function($) { var i = 0; ...

What strategies would you use to put in place conditional imports in a way that is reminiscent of ReactNative

Is there a way to implement conditional imports in other projects similar to how React Native imports Android or iOS specific classes using *.android.js and *.ios.js? If I wanted to have different classes for development and production purposes, could I u ...

Looking for a bootstrap table code that includes checkboxes and a save button, so that when the save button is clicked, it

Seeking a Bootstrap table code that includes row checkboxes. When the save button is clicked, it should return the selected checkbox rows. ...

How to insert text into a text input using onKeyPress in react.js

I am looking to simulate a typing effect where the phrase 'Surveillance Capitalism' is inputted letter by letter into a text input field as a user types. However, I encounter an issue when trying to add each individual letter into the input; I re ...

Update the value of a Vue tree select component using JavaScript

I'm working on a school project using plain JavaScript and needed a tree view select with multiple layers. After extensive searching, I stumbled upon this tool. It's working smoothly, but the one thing that has me stumped is how to change its va ...

What are the best methods for transferring data between child and parent components effectively?

First and foremost, here is the current setup: CHILD COMPONENT // HTML <v-select v-bind:items="selectItems" v-model="selectedItem" label="Category" item-value="text" ></v-select> <v-text-field label="Enter Value" type="number" v-mod ...

Leveraging GSAP and Vue by utilizing props to dynamically calculate a maxWidth

I am working on animating buttons in my application using GSAP. The idea is that when a user clicks the button, it should animate the maxWidth of the button. I want this to be dynamic by adding a percentage of the max width set using props. Is it possibl ...

Is there a better method to determine the width when utilizing the jQuery UI resizable feature for improved efficiency?

I'm currently working on a website that features a resizable sidebar. I want the icons and text within the sidebar to shrink proportionally when the user resizes it. Right now, I have implemented an if statement to check if the sidebar's width fa ...

What is the best way to design a grid with various squares in React Native?

Here's the design I aim to achieve: I am looking to implement the above layout in react native and ensure it is responsive on all screen sizes. I attempted using flexbox but couldn't figure out how to make the boxes square shaped. The code provi ...

locating the truth value of the data in an array retrieved from MongoDB

When returning from the mongoose find() function, I need to ensure that is_reqestor = true is checked. However, when updating the document, I pass the id which needs to be updated. let filter = { is_reqestor: true } if (!is ...

A guide on verifying the presence of a v-data-table element using Cypress

The vuetify v-data-table component includes a feature called "show-select" which adds a checkbox to each list item. I am facing an issue where I need to check elements in the table for a Cypress test, but so far, it has not been successful. I assigned an i ...

JavaScript Function to Retrieve URL Parameter in PHPIn this tutorial

I'm currently working on an HTML5 game where each level has its own dedicated HTML page. The results of each level are saved in a JavaScript variable. My question is, how can I pass this information to the next page using the URL? I was considering ...

The ionChange functionality is not functioning as expected on ion-select elements

Is there a way to have a codeblock executed each time the value of my ion-select changes? The documentation mentions an ionChange event for this purpose. However, when attempting to use it, the event does not trigger. I experimented with the ionFocus ...

Capture a screenshot of the icons

I'm curious about displaying specific parts of images in a React Native application. class InstaClone extends Component { render() { return( <View style={{ flex:1, width:100 + "%", height:100 + "%" }}> <View style={st ...

Scope binding is successful, but accessing the array is only possible after adding an Alert() function

Within my Angular controller, I'm utilizing the SharePoint JavaScript Object Model to fetch data from the Taxonomy (term store). Due to SharePoint's JSOM not being a conventional Angular function that can be easily understood by the scope, I util ...