Solucionado (ver solução)
Solucionado
(ver solução)
5
respostas

[Dúvida] Compartilhar 'methods' entre components

Ao estudar vuex, o professor citou algumas alternativas para gerenciamento de estados, dentre elas a função 'Provider/Inject' onde um componente 'neto' pode acessar propriedades de um componente 'vô', eliminando as props drilling. Minha dúvida é, existe alguma forma de fazer isso, mas com métodos? Em alguns projetos, já me deparei fazendo algo como as props drilling, mas como métodos (this.$emits()), onde o componente neto emite um evento, o pai escuta, e emite novamente para o componente avô. Se alguém conhecer, poderia disponibilizar o link para a documentação, por favor? De já, obrigado.

5 respostas

Salve, Arthur!

Também é possível prover e injetar funções, utilizando a mesma funcionalidade vista em aula. Tentei fazer um exemplo aqui pra tentar deixar mais palpável a implementação, olha:

Aqui é o componente avô, que conhece a implementação:

<template>
  <h1>Componente avô com o método no provide</h1>
  <ComponentePai />
</template>

<script>
import ComponentePai from "./ComponentePai.vue";
export default {
  components: { ComponentePai },
  provide() {
    return {
      realizarAcao: () => {
        console.log("Ação realizada no avô");
      },
    };
  },
};
</script>

Aqui é o componente pai, que não precisa mais fazer nada:

<template>
  <h2>Componente Pai</h2>
  <ComponenteFilho />
</template>

<script>
import ComponenteFilho from "./ComponenteFilho.vue";
export default {
  components: { ComponenteFilho },
};
</script>

E por último o componente filho, que executa a ação cuja a implementação está lá em cima no avô:

<template>
  <h3>Componente Filho</h3>
  <button @click="realizarAcao">Realizar ação</button>
</template>

<script>
export default {
  inject: ["realizarAcao"],
};
</script>

Assim, você consegue prover e injetar funções. Mas cuidado! Dependendo do tamanho da aplicação, pode se tornar um desafio buscar onde esses métodos foram, de fato, implementados. Vale analisar cada caso.

Espero que tenha te ajudado com a sua dúvida!

Bons estudos.

Show de bola. Mas existe uma forma desse método injetado ficar disponível no meu script? Pois, no exemplo que você colocou, onde o método é acionado no template, a função é executada, mas eu não consigo referenciá-la no meu script, para ser executado dentro de uma outra função:

//código omitido
// COMPONENTE FILHO // 
methods: {
funcaoQualquer(){
    this.realizarAcao()
}
}
inject:['realizarAcao']

// código omitido

Sim, é possível. Ainda utilizando o mesmo exemplo, ficaria assim:

<template>
  <h3>Componente Filho</h3>
  <button @click="aoClicado">Realizar açao</button>
</template>

<script>
export default {
  inject: ["realizarAcao"],
  methods: {
    aoClicado () {
        console.log('método chamado no Filho')
        this.realizarAcao()
    }
  }
};
</script>

Mas dessa forma eu recebo o seguinte erro: Imagem de erro

solução!

Esse erro de erro de tipagem é porque você está utilizando TypeScript, e nos exemplos que te mandei eu estava com os componentes configurado para JavaScript. Sem os tipos.

Os mesmos exemplos, em TypeScript. A diferença é que precisamos fazer o setup usando a Composition API:

<template>
  <h1>Componente avô com o método no provide</h1>
  <ComponentePai />
</template>

<script lang="ts">
import { defineComponent } from "@vue/runtime-core";
import ComponentePai from "./ComponentePai.vue";
export default defineComponent({
  components: { ComponentePai },
  provide() {
    return {
      realizarAcao () {
        console.log("Ação realizada no avô");
      },
    };
  },
});
</script>
<template>
  <h2>Componente Pai</h2>
  <ComponenteFilho />
</template>

<script lang="ts">
import { defineComponent } from "@vue/runtime-core";
import ComponenteFilho from "./ComponenteFilho.vue";
export default defineComponent({
  components: { ComponenteFilho },
});
</script>
<template>
  <h3>Componente Filho</h3>
  <button @click="aoClicado">Realizar açao</button>
</template>

<script lang="ts">
import { defineComponent, inject } from "@vue/runtime-core";

export default defineComponent({
  inject: ["realizarAcao"],
  setup () {
    const realizarAcao = inject('realizarAcao') as () => void
    return {
      realizarAcao,
    }
  },
  methods: {
    aoClicado () {
        console.log('método chamado no Filho')
        this.realizarAcao()
    }
  }
});
</script>

Uma outra alternativa:

<template>
  <h3>Componente Filho</h3>
  <button @click="aoClicado">Realizar açao</button>
</template>

<script lang="ts">
import { defineComponent, inject } from "@vue/runtime-core";

export default defineComponent({
  inject: ["realizarAcao"],
  setup () {
    const realizarAcao = inject<() => void>('realizarAcao')
    return {
      realizarAcao,
    }
  },
  methods: {
    aoClicado () {
        console.log('método chamado no Filho')
        if (this.realizarAcao) {
          this.realizarAcao()
        }
    }
  }
});
</script>

Depende de como você quer configurar a tipagem do seu provide/inject. Ainda existem outras formas, como por exemplo, criar interfaces ou tipos.