# Prover e Injetar Dados

Esta página assume que você já leu o Básico sobre Componentes. Leia lá primeiro se você for novo em componentes.

Normalmente, quando precisamos passar dados do componente pai para o filho, usamos propriedades (também conhecidas como props). Imagine a estrutura em que você tem alguns componentes profundamente aninhados e precisa de algo do componente pai apenas num componente filho que está profundamente aninhado. Nesse caso, você precisaria passar a propriedade por toda a cadeia de componentes, o que poderia ser irritante.

Nessas situações, podemos usar o par provide e inject. Os componentes pai podem servir como provedores de dependência para todos os seus filhos, independentemente da profundidade da hierarquia do componente. Este recurso funciona em duas partes: o componente pai tem uma opção provide para fornecer dados e o componente filho tem uma opção inject para usar esses dados.

Provide/inject scheme

Por exemplo, se tivéssemos uma hierarquia como esta:

Raiz
└─ TodoList
   ├─ TodoItem
   └─ TodoListFooter
      ├─ ClearTodosButton
      └─ TodoListStatistics
1
2
3
4
5
6

Se quiséssemos passar o tamanho da lista de tarefas diretamente para TodoListStatistics, passaríamos a propriedade por toda hierarquia: TodoList -> TodoListFooter -> TodoListStatistics. Com a abordagem de prover/injetar, podemos fazer isso diretamente:

const app = Vue.createApp({})

app.component('todo-list', {
  data() {
    return {
      todos: ['Alimentar o gato', 'Comprar ingressos']
    }
  },
  provide: {
    user: 'John Doe'
  },
  template: `
    <div>
      {{ todos.length }}
      <!-- restante do _template_ -->
    </div>
  `
})

app.component('todo-list-statistics', {
  inject: ['user'],
  created() {
    console.log(`Propriedade injetada: ${this.user}`) // > Propriedade injetada: John Doe
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

No entanto, isso não funcionará se tentarmos prover alguma propriedade da instância do componente:

app.component('todo-list', {
  data() {
    return {
      todos: ['Alimentar o gato', 'Comprar ingressos']
    }
  },
  provide: {
    todoLength: this.todos.length // isso resultará no erro `Cannot read property 'length' of undefined`
  },
  template: `
    ...
  `
})
1
2
3
4
5
6
7
8
9
10
11
12
13

Para acessar as propriedades da instância do componente, precisamos converter provide em uma função que retorna um objeto:

app.component('todo-list', {
  data() {
    return {
      todos: ['Alimentar o gato', 'Comprar ingressos']
    }
  },
  provide() {
    return {
      todoLength: this.todos.length
    }
  },
  template: `
    ...
  `
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Isso nos permite continuar desenvolvendo esse componente com mais segurança, sem medo de que possamos alterar/remover algo em que um componente filho dependa. A interface entre esses componentes permanece claramente definida, similar à props.

Na verdade, você pode pensar na injeção de dependência como uma espécie de "props de longo alcance", exceto:

  • os componentes pai não precisam saber quais descendentes usam as propriedades que ele fornece
  • os componentes filhos não precisam saber de onde vêm as propriedades injetadas

# Trabalhando com Reatividade

No exemplo acima, se alterarmos a lista de tarefas, essa alteração não será refletida na propriedade injetada todoLength. Isso ocorre porque as interligações provide/inject não são reativas por padrão. Podemos mudar esse comportamento passando uma propriedade ref ou objeto reativo (reactive) para provide. Em nosso exemplo, se quiséssemos reagir às mudanças no componente ancestral, precisaríamos atribuir um dado computado (usando o computed da API de Composição) para nosso todoLength fornecido:

app.component('todo-list', {
  // ...
  provide() {
    return {
      todoLength: Vue.computed(() => this.todos.length)
    }
  }
})

app.component('todo-list-statistics', {
  inject: ['todoLength'],
  created() {
    console.log(`Propriedade injetada: ${this.todoLength.value}`) // > Propriedade injetada: 5
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Assim, qualquer alteração em todos.length será refletida corretamente nos componentes, onde todoLength é injetado. Leia mais sobre computed na seção Dados Computados e Observadores e prover e injetar dados usando reactive na seção da API de Composição.