Vue 3: Como funcionam os Renderless Components.
Tags: braziliandevs, programming, tutorial, vue
Published: 2022-10-02 18:57:06

## O que são _Renderless Components_?
Um componente _renderless_ nada mais é do que um componente que não possui HTML próprio, ele existe apenas para estruturar e/ou tratar informações que são passadas para ele.
Resumidamente, você separa a lógica da UI do seu _app_,  utilizando _slots_.
Isso permite que haja uma flexibilidade no jeito em que as coisas são renderizadas e a lógica passa a ser reutilizável.

## E como eles funcionam?
### Let's code!

Pra entender como esses componentes funcionam, imagine que você deseja criar uma to-do list igual a essa da imagem:

![Essa imagem possui um fundo azul escuro, quase preto e escrito em branco o título da aplicação em inglês "Todo list Example", abaixo tem o input para adicionar uma nova tarefa e logo em seguida, as tarefas já adicionadas anteriormente também escritas em branco, com a checkbox em verde quando selecionada e em branco quando não selecionada.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0wg32exogfjxd2zavrpg.png)

Primeiro vamos começar criando um componente super simples chamado `TodoList.vue`.

Dentro da tag script, vamos criar um array com alguns ítens e duas funções. Uma para marca-los dependendo se status for "true" ou "false" e outra para adicionar novos ítens ao array:

```vue
<script setup>
import { ref } from "vue";

const todos = ref([
  { id: 0, title: "Create an article about renderless components", done: false },
  { id: 1, title: "Write some code", done: false },
  { id: 2, title: "Publish the article on linkedin", done: false },
]);

const toggleTodo = (id) => {
  const todo = todos.value.find((todo) => todo.id === id);
  if (todo) {
    todo.done = !todo.done;
  }
};
const addTodo = (event) => {
  const data = new FormData(event.target);
  event.target.reset();
  todos.value.push({
    id: todos.value.length,
    title: data.get("title"),
    done: false,
  });
};
</script>
```

Agora, ainda no arquivo `TodoList.vue`, vamos criar o template responsável por renderizar esses ítens e executar as funções:

```vue
<template>
  <ul>
    <li>
      <form @submit.prevent="addTodo" class="mb-6 mt-3">
        <input
          type="text"
          name="title"
          class="bg-transparent border-b-2 
          outline-none accent-green-300"
          placeholder="New Todo"
        />
      </form>
    </li>
    <li v-for="todo in todos" :key="todo.title" class="py-1">
      <input
        class="accent-green-300"
        type="checkbox"
        :value="todo.done"
        @click="toggleTodo(todo.id)"
      />
      {{ todo.title }}
    </li>
  </ul>
</template>
```
O código inteiro do arquivo `TodoList.vue` ficará assim:

```vue
<script setup>
import { ref } from "vue";

const todos = ref([
  {
    id: 0,
    title: "Create an article about renderless components",
    done: false,
  },
  { id: 1, title: "Write some code", done: false },
  { id: 2, title: "Publish the article on linkedin", done: false },
]);

const toggleTodo = (id) => {
  const todo = todos.value.find((todo) => todo.id === id);
  if (todo) {
    todo.done = !todo.done;
  }
};
const addTodo = (event) => {
  const data = new FormData(event.target);
  event.target.reset();
  todos.value.push({
    id: todos.value.length,
    title: data.get("title"),
    done: false,
  });
};
</script>
<template>
  <ul>
    <li>
      <form @submit.prevent="addTodo" class="mb-6 mt-3">
        <input
          type="text"
          name="title"
          class="bg-transparent border-b-2 
          outline-none accent-green-300"
          placeholder="New Todo"
        />
      </form>
    </li>
    <li v-for="todo in todos" :key="todo.title" class="py-1">
      <input
        class="accent-green-300"
        type="checkbox"
        :value="todo.done"
        @click="toggleTodo(todo.id)"
      />
      {{ todo.title }}
    </li>
  </ul>
</template>
```


Repare que esse componente possui toda a lógica para criar a nossa _to-do list_ e também possui a UI responsável por exibi-la.

Então, a partir de agora vamos separar tudo, passando toda a lógica de pai pra filho, utilizando _slots_.

>Mas antes disso, se você não sabe o que são slots, você pode dar uma olhadinha na documentação oficial do Vue.js: [Slots](https://vuejs.org/guide/components/slots.html)

A primeira coisa que vamos fazer é criar um componente pai e importar nosso componente filho `TodoList.vue` dentro dele.

>✨Dica: Você pode utilizar o arquivo `App.vue`, que já existe por padrão, para importar o componente `TodoList`.✨

```vue
<script setup>
import TodoList from "./components/TodoList.vue";

</script>
<template>
  <main>
     <TodoList />
  </main>
</template>
```

Depois, no componente filho `TodoList.vue`, vamos substituir o código por um `<slot>` e então fazer um bind com os nossos _to-do's_ e as funções que criamos, dessa forma:

```vue
<template>
  <ul>
    <slot 
    :todos="todos" 
    :toggleTodo="toggleTodo" 
    :addTodo="addTodo">
    </slot>
  </ul>
</template>

```

Agora precisamos fazer com que o componente pai tenha acesso aos dados do componente filho, então voltamos ao componente pai e adicionamos ao componente `<TodoList/>` um v-slot, passando nossa lista de _to-do's_ e as funções `addTodo ` e `toggleTodo`, dessa forma:

```vue
<TodoList v-slot="{ todos, addTodo, toggleTodo }">
 //...
</TodoList>

```
A partir daí, podemos adicionar o HTML que removemos do componente `TodoList.vue` ao nosso v-slot:

```vue
<TodoList v-slot="{ todos, addTodo, toggleTodo }">
        <li>
          <form @submit.prevent="addTodo" class="mb-6 mt-3">
            <input
              type="text"
              name="title"
              class="bg-transparent border-b-2 outline-none accent-green-300"
              placeholder="New Todo"
            />
          </form>
        </li>
        <li v-for="todo in todos" :key="todo.title" class="py-1">
          <input
            class="accent-green-300"
            type="checkbox"
            :value="todo.done"
            @click="toggleTodo(todo.id)"
          />
          {{ todo.title }}
        </li>
      </TodoList>
```

Basicamente o código que antes estava dentro do componente `TodoList.vue`, agora ficará dentro do `v-slot` no componente pai.

Agora o componente pai ficará responsável por "injetar" as informações no componente filho e isso é o que vai permitir que a lógica seja reutilizada para a criação de novas listas, que podem possuir uma UI completamente diferente da que criamos nesse exemplo.

Você pode tentar modificar a UI, deixando totalmente diferente e me falar depois o que achou :)

Muito legal, né? Espero que você tenha entendido e gostado de ler até aqui, pois essa foi a forma que encontrei de fixar conteúdos na minha cabeça e ainda contribuir de alguma forma com a comunidade.

Grande abraço e tchauuu

<img width="100%" style="width:100%" src="https://media.giphy.com/media/xT3JvPQibFu3C/giphy-downsized-large.gif">