Search code examples
javascriptvue.jsvuexvue-native

Vuex store state not updating screen / Vue-Native


I'm using Vue-Native to build a simple Application with multiple Screens (using Vue Native Router). And I have the situation where I connect to a WebSocket in Screen A which listens for messages and I need these updates to be available in Screen A and Screen B.

So after I had no luck with global variables and prototype properties, I came across Vuex which seems to do exactly what I need.

And indeed it updates the properties across the screens fine but it does not seem to be reactive and update the screen.

store.js:

import Vue from "vue-native-core";
import Vuex from "vuex"
Vue.use(Vuex);

export default new Vuex.Store({
    state: {
        imageUri: ["", "", "", ""]
    },
    mutations: {
      updateImage (state, data) {
        state.imageUri[data.index] = data.url;
      }
    }
  });

ScreenA.vue in script-Tag:

import store from "./store.js"

export default {
  [...]
  methods: {
    [...]
    handleMessage: function(message){
      var data = message.data.split("#", 2);
      var value = data[1];
      console.log("New msg");


      if(data[0] == "init"){
        this.connectionMs = Date.now()-value;
        this.connectionStatus = 2;
      }else if(data[0] == "img"){
        var current = this.cImg;
        this.cImg = (this.cImg+1)%4;
        var dataUrl = "data:image/jpeg;base64,"+value.substring(2, value.length-1);
        store.commit('updateImage', {index: current, url: dataUrl}); //<- Relevant line
      }
    },
    [...]
  }
}

ScreenB.vue:

<template>
    <view :style="{marginTop: 40}">
        <image resizeMode="contain" :style="{ width: '100%', height: 200 }" :source="{uri: imageUri[0]}"/>
        <image resizeMode="contain" :style="{ width: '100%', height: 200 , marginTop: -200}" :source="{uri: imageUri[1]}"/>
        <image resizeMode="contain" :style="{ width: '100%', height: 200 , marginTop: -200}" :source="{uri: imageUri[2]}"/>
        <image resizeMode="contain" :style="{ width: '100%', height: 200 , marginTop: -200}" :source="{uri: imageUri[3]}"/>
        <touchable-opacity :on-press="btnPress">
            <text>Press me! {{imageUri[0]}}</text>
        </touchable-opacity>
    </view>
</template>

<script>
import store from "./store.js"

export default {
    props: {
        navigation: {
            type: Object
        }
    },
    computed:{
        imageUri: function(){
            return store.state.imageUri;
        }
    },
    methods: {
        btnPress: function(){
            console.log("ImgUrl0 -> "+this.imageUri[0]);
        },
    },
}
</script>

The computed property is updating correctly as soon as the vuex state in the store changes (console.log prints new value) but the rendered data on the screen (Text- & Image-Elements) remains with the old data.

Is there any way to solve this issue? Maybe a completely different approach to sync my dynamic data across screens?


Solution

  • Your mutation only update state.imageUri[data.index], it will not change reference of state.imageUri. That means state.imageUri still point to old reference, and Vue can't not detect this update. It's one of Vue's gotchas

    One solution is using JSON.parse(JSON.stringify()) to make a deep copy of state.imageUri array

     export default new Vuex.Store({
            state: {
                imageUri: ["", "", "", ""]
            },
            mutations: {
              updateImage (state, data) {
                state.imageUri[data.index] = data.url;
                state.imageUri = JSON.parse(JSON.stringify(state.imageUri))
              }
            }
          });