When writing imperative code, we might have a shared state but we only want it to be accessible within a logical scope. Say for example in the following JavaScript snippet:
function doSomethingUseful() {
const state = {
propOne: "",
propTwo: false,
};
doSomethingWithState(state);
doSomethingWithPropOne(state.propOne);
doSomethingWithPropTwo(state.propTwo);
}
we can see that state
is only accessible within the doSomethingUseful()
function and not accessible outside.
Motivation
It would be useful we can do the above declaratively in the <template>
without the hassle of creating a new component or having the parent component to store the state. Another good use-case is within an iteration in a loop construct (v-for
), it would lead to messy code if we want to track the state in each loop iteration.
MiniStore
component
The MiniStore
component is very straightforward to create (really!).
<template>
<slot :scope="scope"></slot>
</template>
<script lang="ts" setup>
import { reactive } from "vue";
const props = defineProps<{
data: Record<string, any>;
}>();
const scope = reactive(props.data);
</script>
In less than 15 lines of code, we can create a MiniStore
component, powered by the reactive()
function of the reactivity API. The state passed in via the props are reactive by default, which make them useful when data-binding to components.
Usage
Let’s see how we make use of MiniStore
.
<script setup lang="ts">
import MiniStore from "./MiniStore.vue";
import Alerter from "./Alerter.vue";
</script>
<template>
<MiniStore :data="{ isChecked: false }" v-slot="{ scope }">
<label for="agree"
><input
id="agree"
type="checkbox"
v-model="scope.isChecked"
class="mr-4"
/>I agree to the terms and conditions</label
>
<Alerter ref="alerter" :show="scope.isChecked" />
</MiniStore>
</template>
The idea here is to keep the confined state near the components that are using it, and prevent it from polluting the parent component. As demonstrated here, we can exclude the isChecked
state from declaring on the parent component and “scope” it just for the input
and Alerter
components.
Demo
Clicking on the checkbox here will have the alert dialog show up.
Conclusion
The VueJS reactivity API is really powerful when we harness it like the MiniStore
component we described here. Moreover, we are able to keep our code cleaner without declaring components-only coupled state on the parent component.