i have this component, if nav
is entered or leaved, i want to reassign isNavClosed
to the opposite of its value so nav
and main
content get decreased and increased accordingly, but i only want to do that if hamburger menu is closed, in nav
component i have ref isHamburger
to express the state of hamburger menu, so in enterLeaveNav
function i want to reassign isNavClosed
ref only if ref isHamburger
is true, therefore i need to check its value from nav
child before reassigning it, how can i access the value of isHamburger
from parent without actually moving it to the parent ?
<script setup>
import Header from "@/Components/Header.vue";
import Nav from "@/Components/Nav.vue";
import { ref } from "vue";
const isScroll = ref(false);
const isNavClosed = ref(false);
function setIsNavClosed() {
isNavClosed.value = !isNavClosed.value;
}
function enterLeaveNav() {
isScroll.value = !isScroll.value;
}
</script>
<template>
<div>
<!-- Page Heading -->
<header class="header container">
<Header />
</header>
<div class="container-fluid">
<div class="row g-0">
<!-- navigation component -->
<nav
class="p-0 nav"
:class="[
isScroll ? 'overflowScroll' : 'overflowHidden',
isNavClosed ? 'width833' : 'width1633',
]"
@mouseenter="enterLeaveNav"
@mouseleave="enterLeaveNav"
>
<Nav @open-close-nav="setIsNavClosed" />
</nav>
<!-- Main content-->
<main
class="ps-5"
:class="[
isNavClosed
? 'offset-md-1 col-md-10'
: 'offset-md-2 col-md-9',
]"
style="margin-top: 100px"
>
<slot name="content" />
</main>
</div>
</div>
</div>
</template>
i tried to use composable to use these states accross child and parent component, but the problem is that when nav
component mutate state, only nav
component will be updated, but the parent will not be updated and the state will always be false:
Composables/useNavigation.js
import { ref } from "vue";
export function Navigation() {
const isNavClosed = ref(false);
const isHamburgerClosed = ref(false);
const isScroll = ref(false);
function toggleNav() {
isNavClosed.value = !isNavClosed.value;
isHamburgerClosed.value = !isHamburgerClosed.value;
}
function enterLeaveNav() {
if (isHamburgerClosed.value) {
isNavClosed.value = !isNavClosed.value;
}
isScroll.value = !isScroll.value;
}
return {
isScroll,
isNavClosed,
isHamburgerClosed,
toggleNav,
enterLeaveNav,
};
}
Nav.vue
<div
class="hamburgerMenu"
:class="isHamburgerClosed ?
'hamburgerMenuMarginLeft2' :
'hamburgerMenuMarginLeft80'"
@click="toggleNav">
<div class="hamburgerPiece"></div>
<div class="hamburgerPiece"></div>
<div class="hamburgerPiece"></div>
</div>
parent component
<script setup>
import Header from "@/Components/Header.vue";
import Nav from "@/Components/Nav.vue";
import { Navigation } from "@/Composables/Navigation";
const { isScroll, isHamburgerClosed, isNavClosed, enterLeaveNav } =
Navigation();
</script>
<!-- @open-close-nav="setIsNavClosed" -->
<!-- :class="[isNavClosed ? 'offset-md-1' : 'offset-md-2']" -->
<template>
<div>
<!-- Page Heading -->
<header class="header container">
<Header />
</header>
<div class="container-fluid">
<div class="row g-0">
{{ isHamburgerClosed }}
<nav
class="p-0 nav"
:class="[
isScroll ? 'overflowScroll' : 'overflowHidden',
isNavClosed ? 'width833' : 'width1633',
]"
@mouseenter="enterLeaveNav"
@mouseleave="enterLeaveNav"
>
<Nav />
</nav>
<main
class="ps-5"
:class="[
isNavClosed
? 'offset-md-1 col-md-10'
: 'offset-md-2 col-md-9',
]"
style="margin-top: 100px"
>
<slot name="content" />
</main>
</div>
</div>
</div>
</template>
Since you parent/child components aren't intended to use separately you probably should use provide/inject
with the state in the parent component. I love it and use it always in parent-child components that are used only together:
https://vuejs.org/guide/components/provide-inject.html
Also note that the global state is for business data. The UI state (expanded / collapsed) that lives only when its components are alive should be stored in the root component of a component hierarchy.