Type-safe context injections in Vue.js (a-la React)
1 min readJul 5, 2024
I find provide
and inject
in Vue.js to be quite limiting, so I wrote some syntactic sugar to get the most out of them. I do prefer context
providers in React, so you will notice some similarities.
Thanks to this method, I do not need to pollute my global scope and can contextualize my state as needed. This inversion of control makes testing much simpler, and allows me to reuse components across the app without worrying much about leaking state.
Provider Factory
import { inject, provide } from 'vue';
export const defineProvider = <T, A extends unknown[]>(name: string, fn: (...args: A) => T) => {
const injectionKey = Symbol(name);
return {
provide: (...args: Parameters<typeof fn>) => {
const ctx = fn(...args);
provide<T>(injectionKey, ctx);
return ctx;
},
inject: () => {
const ctx = inject(injectionKey);
if (!ctx) {
throw new Error('You must create a context before using it');
}
return ctx as T;
},
};
};
Context Factory
const createMyContext = (b: string) => {
return {
a: 1,
b
}
}
const { provide, inject } = defineProvider('MyContext', createMyContext);
export { provide as provisionMyContext, inject as useMyContext };
Provider Component
<script setup>
import { provisionMyContext } from './context'
const ctx= provisionMyContext(b)
</script>
<template>
b is {{ ctx.b }}
</template>
Child Component
<script setup>
import { useMyContext } from './context'
const ctx= useMyContext()
</script>
<template>
b is {{ ctx.b }}
</template>
Bisous.