darkmode with vuex and save in localstorage

有些话、适合烂在心里 提交于 2021-02-10 14:21:51

问题


I have a chrome bug telling me Write operation failed: computed property "isDark" is readonly.

my mutation create a DarkMode key in the locastorage and I also create a store that checks if DarkMode exists in the localestorage

it works but if I refresh the page the mutation is inverted for example if in the localestorage darkMode is true if I click on the toggle the mutation is also a true then what should return false

My Page

  <div id="home">
    <div class="toggleTheme">
      <ToggleBtnTheme
        labelOn="Dark"
        labelOff="Light"
        v-model:value="isDark"
        @input="mode"
      />
    </div>
    <div class="title">
      <h1 :class="isDark ? 'dark' : 'light'">Lidian Manoha</h1>
    </div>
  </div>
</template>

<script>
import { mapMutations, mapGetters } from 'vuex';
import ToggleBtnTheme from '@/components/atoms/ToggleBtn/index';

export default {
  name: 'Home',
  components: {
    ToggleBtnTheme
  },

  methods: {
    ...mapMutations('themeDarkMode', ['toggleDarkMode']),
    mode() {
      this.toggleDarkMode();
    }
  },

  computed: {
    ...mapGetters('themeDarkMode', ['isDark'])
  },
  watch: {}
};
</script>

<style lang="scss" src="./style.scss" scoped></style>```

My Stores

    ```   return {
          darkMode: true,
          isDark: false
        };
      },
      getters: DarkModeGetters,
      mutations: DarkModeMutations,
      action: DarkModeAction
    };
    
    ```

My getters.js

    ```
    export default {
      isDark: state => state.isDark
    };
    ```

My mutations.js

    ```
    export default {
      toggleDarkMode: state => {
        state.isDark = !state.isDark;
        console.log('mutation', state.isDark);
        localStorage.setItem('DarkMode', JSON.stringify(state.isDark));
      }
    };```

And my ToggleBtn

    ```<template>
      <div id="toggleBtn">
        <div class="toggleBtnContainer">
          <label class="label" v-if="label">{{ label }}</label>
          <div class="containerToggle">
            <input
              v-bind="$attrs"
              :id="`toggle${uuid}`"
              :disabled="disabled"
              type="checkbox"
              :checked="value || checked"
              @change="$emit('update:value', $event.target.checked)"
            />
            <label
              :for="`toggle${uuid}`"
              v-html="value ? labelOn : labelOff"
              @click="$emit('click', $event)"
            ></label>
          </div>
        </div>
      </div>
    </template>
    
    <script>
    import { uuid } from 'vue-uuid';
    
    export default {
      name: 'toggleBtn',
      inheritAttrs: false,
      props: {
        value: { type: [Boolean, String, Number, Function], default: false },
        checked: { type: Boolean, default: false },
        labelOn: { type: String, default: 'off' },
        labelOff: { type: String, default: 'on' },
        label: { type: String, default: null },
        disabled: { type: Boolean, default: false }
      },
      data() {
        return {
          uuid: uuid.v4()
        };
      }
    };
    </script>
    
    <style lang="scss" src="./style.scss" scoped></style>```


回答1:


Thats probably because you missed a point. When you refresh the page...ie the app reloads... if you find that there is a data('Darkmode') available in the local storage then you need to set it through the mutation in the first place. By doing so what happens is when you refresh the page...if darkmode is already set to true then your vuex state('isDark') will also be set to true. Therefore when you click on that it will toggle as expected.

Its better to have two mutations to avoid confusions

My mutations.js

   
    export default {
      toggleDarkMode: state => {
        state.isDark = !state.isDark;
        console.log('mutation', state.isDark);
        localStorage.setItem('DarkMode', JSON.stringify(state.isDark));
      },
      initializeDarkMode: (state, payload) => {
        state.isDark = payload; // payload is actually the value that was stored in the local storage
      }
    };

When the app loads, just call the second mutation to initialize the state with the local storage value.

  this.initializeDarkMode(DarkMode);  // DarkMode contains the value fetched from local storage

Below I have modified the code to call the second mutation

  <div id="home">
    <div class="toggleTheme">
      <ToggleBtnTheme
        labelOn="Dark"
        labelOff="Light"
        v-model:value="isDarkMode" // ****************Changes added ***************** 
        @input="mode"
      />
    </div>
    <div class="title">
      <h1 :class="isDark ? 'dark' : 'light'">Lidian Manoha</h1>
    </div>
  </div>
</template>

<script>
import { mapMutations, mapGetters } from 'vuex';
import ToggleBtnTheme from '@/components/atoms/ToggleBtn/index';

export default {
  name: 'Home',
  components: {
    ToggleBtnTheme
  },
  data() {
    return {
     isDarkMode: this.isDark,
     },
   
  watch: {
     isDark(newVal) {
       this.isDarkMode = newVal;
     }
  },
// ****************Changes added below ***************** 
  mounted() { 
   const isDark = localStorage.getItem('DarkMode');
   if(isDark && isDark === 'true') {
       this.initializeDarkMode(true); 
       this.isDarkMode = true;
    } else {
      this.initializeDarkMode(false);
      this.isDarkMode = false;
  }
  methods: {
    ...mapMutations('themeDarkMode', ['toggleDarkMode', 'initializeDarkMode']),
    mode() {
      this.toggleDarkMode();
    }
  },

  computed: {
    ...mapGetters('themeDarkMode', ['isDark'])
  },
  watch: {}
};
</script>

<style lang="scss" src="./style.scss" scoped></style>


来源:https://stackoverflow.com/questions/65807129/darkmode-with-vuex-and-save-in-localstorage

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!