Home  >  Q&A  >  body text

Type reference provision/injection functionality in VueJS

<p>I'm trying to provide a typed ref from a parent component and inject it into the child component. </p> <p>I'm not sure if I understand the VueJS documentation, but this is what I came up with. </p> <pre class="brush:php;toolbar:false;">import type { Ref } from 'vue' import type { InjectionKey } from 'vue' import type DocumentSet from '@/types/DocumentSet' const documentSet = Symbol() as InjectionKey<{ documentSet: Ref<DocumentSet>, update: () => void }> export default documentSet</pre> <p>So, the task is to provide a typed object (in my case a documentSet), and a function that updates it. </p> <p>Now, in the parent component I have this code: </p> <pre class="brush:php;toolbar:false;">import dsKey from '@/injectionKeys/documentSet' import type DocumentSet from '@/types/DocumentSet' ... const documentSet = ref<DocumentSet | null>(null) provide(dsKey, { documentSet, update: () => console.log('update') })</pre> <p>And in the child component I have this code: </p> <pre class="brush:php;toolbar:false;">import dsKey from '@/injectionKeys/documentSet' const ds = inject(dsKey) const documentSet = ds?.documentSet</pre> <p>The problem is, since there is a lot of boilerplate code, I feel like I'm doing something wrong. For example, I can't understand why the ds object should be refactored like this: </p> <pre class="brush:php;toolbar:false;">const { documentSet } = ds</pre> <p>Is it possible to shorten the code instead of writing <code>ds?.documentSet</code>, especially if I'm sure that ds should exist (without it I won't render the child component)</p> <p>If I write <code>const { documentSet } = inject(dsKey)</code>, the following error will appear: </p> <p><code>TS2339: Property 'documentSet' does not exist on type '{ documentSet: Ref ;' Update: () => Invalid; } | Undefined '.</code></p> <p>The idea is because I want to eliminate duplication in child components: </p> <pre class="brush:php;toolbar:false;"><DocumentSetInfo :documentSet="documentSet" /> <DocumentSetMemos :documentSet="documentSet" /> <DocumentSetPrograms :documentSet="documentSet" /> <DocumentSetPetition :documentSet="documentSet" /> <DocumentSetPassRequests :documentSet="documentSet" /> <DocumentSetReport :documentSet="documentSet" /> <DocumentSetReceptionNotices :documentSet="documentSet" /></pre></p>
P粉441076405P粉441076405434 days ago535

reply all(1)I'll reply

  • P粉446800329

    P粉4468003292023-09-05 09:24:21

    You can put this into a composable that provides a function provde that handles provision and a function use* that handles injection:

    const CurrentDocumentSetKey = Symbol() as InjectionKey<{
      documentSet: Ref<DocumentSet>,
      update: () => void
    }>
    
    export function provideCurrentDocumentSet(documentSet:DocumentSet, update: () => any){
      provide(CurrentDocumentSetKey, {documentSet, update})
    }
    
    export useCurrentDocumentSet(){
      const ds = inject(CurrentDocumentSetKey)
      if (!ds) throw new Error('No current DocumentSet provided')
      return ds
    }

    Now you only need to call provideCurrentDocumentSet() in the parent component, and all child components only need to perform the following operations:

    const {documentSet, update} = useCurrentDocumentSet()

    You don't even need to export the InjectionKey, and the component doesn't need to know about it. (For example, Vuetify also does this, for example here)

    reply
    0
  • Cancelreply