Reputation: 1
I have a navbar component and a view component. When a user logs in, I want both components to re-render. Currently only the view component re-renders, the navbar doesn't.
Tried force re-render with :key passing a "user" object, passing a "navKey" counter that updates on changes. Regardless, I always get "[Vue warn]: Invalid watch source: true A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types."
Read dozens of guides and docs and spent days on this but I just don't get what I'm missing. First app, appreciate your help.
App.vue
<template>
<div v-if="appReady" class="min-h-full font-Poppins box-border">
<Navigation :key="state" />
<router-view />
</div>
</template>
<script>
import Navigation from "./components/Navigation.vue";
import { ref, reactive } from "vue";
import store from "./store/store.js";
import { useRouter } from "vue-router";
export default {
components: {
Navigation,
},
setup() {
// Data & variables
const appReady = ref(null);
const router = useRouter()
const user = JSON.parse(localStorage.getItem("BJJFocusUser"))
const state = store.methods.setUser(user)
if (!user) {
appReady.value = true;
console.log("No user logged in");
router.push({ name: "Login" });
} else {
store.methods.setUser(user);
appReady.value = true;
console.log("User logged in");
router.push({ name: "ProgressView" });
}
return { appReady, user, state };
},
};
</script>
Navigation.vue
<template>
<header class="bg-at-light-orange text-white">
<nav
class="container py-5 px-4 flex flex-column gap-4 items-center sm:flex-row"
>
<div class="flex items-center gap-x-4">
<img
class="w-32"
src="../assets/vector/default-monochrome-white.svg"
alt="bjj focus logo"
/>
</div>
<Slide
</Slide>
</nav>
</header>
</template>
<script>
import { logoutUser } from "../services/userService";
import { useRouter } from "vue-router";
import { Slide } from "vue3-burger-menu"
import store from "../store/store"
export default {
components: {
Slide
},
setup() {
const router = useRouter();
const user = store.state.user
// Logout function
const logout = async () => {
logoutUser();
store.methods.setUser()
router.push({ name: "Login" });
};
return { logout, Slide, store, user };
},
};
</script>
Store.js
import { reactive } from "vue";
const state = reactive({
user: null,
});
const methods = {
setUser(payload) {
state.user = payload ? payload : null;
},
getUser() {
return JSON.stringify(state.user)
}
};
export default {
state,
methods,
};
Upvotes: 0
Views: 1698
Reputation: 1
The way I managed to make the navbar re-render was:
emitLogin()
// Emitter (EventBus) this section emits an event that can be listened to globally
const emitter = inject('emitter')
const emitLogin = _ => {
emitter.emit('userHasLoggedIn', true)
}
emitLogout()
// Emitter (EventBus) this section emits an event that can be listened to globally
const emitter = inject('emitter')
const emitLogout = _ => {
emitter.emit('userHasLoggedOut', true)
}
:key
that reads a variable that in turn gets updated when the listener receives the emitter's signal<template>
<div v-if="appReady" class="min-h-full font-Poppins box-border bg-at-light-orange">
<Navigation :key="navRerenderKey" /> // **NOTE THE :key HERE**
<router-view />
</div>
</template>
<script>
export default {
setup() {
// Initialize this variable before the functions
const navRerenderKey = ref(0)
// Listener (EventBus) this section listens to the emitters
const emitter = inject('emitter')
emitter.on('userHasLoggedIn', (value) => {
navRerenderKey.value += 1
console.log("(emitter) Logged In")
})
emitter.on('userHasLoggedOut', (value) => {
navRerenderKey.value += 1
console.log("(emitter) Logged Out")
})
}
}
</script>
Upvotes: 0