Storing Fabric.js Canvas reference in Vuex - [vuex] do not mutate vuex store state outside mutation handlers

Viewed 346

I'm creating a Vue Application that uses Fabric.js to manage the canvas.

I want to manage the canvas state using Vuex. For this, I set the fabric canvas instance to a vuex state variable.

Mutation

setCanvas(state, canv) {
    state.canvas = canv
  }

In Vue Component

const canvas = new fabric.Canvas('main-canvas')
this.setCanvas(canvas) //commit the mutation

In another mutation I have to add an object to the canvas:

addLayer(state, layer) {
    state.canvas.add(layer)
 }

When running this mutation I get the following error:

[vuex] do not mutate Vuex store state outside mutation handlers.

I think the reason is that fabric js instance is editing props in itself. And vuex does not seem to like this.

What is the best way to store the fabric object for this problem ? or is there a better way to solve it?

2 Answers

It is not ideal to store it in the vuex, I know whhy you would want to do that. Probably because several components around your app would want to call the canvas methods.

It seems to store it copies over events that are fired and change the object, which technically is a mutation outside the mutation handler.

Try handling everything that has to do with the initialization of the canvas in one component (In the "mounted" method), then you can use watches of Vuex states to detect things like when a button for adding a circle is pressed, then you can react to it in that component. At least that is how I have handled similar cases. Will update this if I find a better way.

Also, share if you have found a better way.

With Vue3, I will approach that by using the provide/inject feature to create a global property:

so in the main.js file:

// src/main.js
import { createApp, reactive } from 'vue'
import App from './App.vue'

const gobalsProperties = {'canvas': null}; 

const app = createApp(App);
app.provide('gobalsProperties', gobalsProperties)
app.mount('#app');

Then it needs to be injected to all the components where your gonna use it like this

export default {  
  inject: ['gobalsProperties'],
  ...

And it can be accessed in the script with this.gobalsProperties. So in the component where you have the <canvas \> tag, you can initiate this.gobalsProperties.canvas in the mounted function like so:

  mounted() { 
    this.gobalsProperties.canvas = new fabric.Canvas('main-canvas')
    ...
Related