javascripthtmlvue.jsvuejs3

Picture isn't changing dynamically with light on/off button on website


I'm trying to change a picture (daytime picture vs nighttime picture) dynamically when a person hits the light button. It works, but I have to refresh the page for the picture to show. Daytime and nighttime are basically just booleans of 'true' or 'false' for my theme variable.

I tried coding it this way:

useTheme.js:

import { ref, watchEffect } from 'vue';

export default function useTheme() {
    const theme = ref(JSON.parse(localStorage.getItem('theme') || 'false'));

    watchEffect(() => {
        localStorage.setItem('theme', String(theme.value));
    });

    return { theme };
}

App.vue:

const { theme } = useTheme(); // Access the shared state.

const toggleTheme = () => {
  theme.value = !theme.value;
  document.body.classList.toggle('dark-theme', theme.value);
  localStorage.setItem('theme', JSON.stringify(theme.value));
}

Home.vue (Where the problem resides):

<img :src="banner" alt="Daytime Banner">
import DayTimeBanner from "@/assets/banner-city-daytime.png";
import NightTimeBanner from "@/assets/banner-city-nighttime.png";
...
const { theme } = useTheme(); // Access the same shared state.

const banner = computed(() => theme.value ? NightTimeBanner : DayTimeBanner);

Any clue on what might be going on? Changes to the theme background is reactive, but for the banner, I have to refresh the page for the image to pop up.


Solution

  • The error is that in your useTheme.js file you are declaring the 'theme' state inside the function, so you are creating a local state each time you call the useTheme() function instead of a global one.

    Change your code to this:

    import { ref, watchEffect } from 'vue';
    
    const theme = ref(JSON.parse(localStorage.getItem('theme') || 'false'));
    
    watchEffect(() => {
        localStorage.setItem('theme', String(theme.value));
    });
    
    export default function() {
       return { theme };
    }