# Dec 8th, 2021: \[pt-br\] Interface Otimista no Kibana com React Query

**URL:** <https://discuss.elastic.co/t/dec-8th-2021-pt-br-interface-otimista-no-kibana-com-react-query/290812>\
**Category:** Advent Calendar\
**Created:** [December 8, 2021, 8:00am UTC](https://discuss.elastic.co/t/dec-8th-2021-pt-br-interface-otimista-no-kibana-com-react-query/290812 "2021-12-08T08:00:17Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![Paulo\_Henrique](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/paulo_henrique/32/133789_2.png) [@Paulo\_Henrique](https://discuss.elastic.co/u/Paulo_Henrique)\
**Post date:** [December 8, 2021, 8:00am UTC](https://discuss.elastic.co/t/dec-8th-2021-pt-br-interface-otimista-no-kibana-com-react-query/290812/1 "2021-12-08T08:00:17Z")

</div>

Esse artigo também está disponível em [Inglês](https://discuss.elastic.co/t/289214)

### _"Como o uso de Optimistic UI (Interface Otimista), pode melhorar a experiência para os usuários."_

## Introdução

Se você já ouviu falar da biblioteca [React Query](https://react-query.tanstack.com/overview), deve saber o quão fácil é de utilizar, além de dar ao desenvolvedor controle total sobre _data fetching_, _cache_, e dados retornados do servidor. Além disso, você pode facilmente implementar Optimistic UI com React Query.

Optimistic UI (Interface Otimista) é uma técnica já conhecida, que faz com que a Interface do Usuário (UI) mostre _feedback_ imediato das ações que o usuário executa, levando sempre em consideração que a requisição será bem sucedida, essenciamente está relacionada a Mutação de Dados, que é quando os usuários alteram dados que estão no servidor, ou performam _side-effects_ no servidor. Exemplos de ténicas de Optimistic UI são aplicativos de chat, como Whatsapp e Messenger, que quando o usuário clica para enviar a mensagem, a mensagem aparece imediamente na Interface, mesmo que a requisição para o servidor sequer tenha sido iniciada.

Para esse exemplo vamos utilizar uma TODO list (lista de tarefas), porque cobre todos os exemplos necessários de _data-fetching_ e mutações de dados (criar/alterar/excluir).

## No formato tradicional

Quando o usuário clica no botão de Add Todo (Criar Tarefa), a interface inicia uma requisição para o servidor e aguarda a resposta. Uma vez que o servidor responde, a interface verifica qual foi o tipo de resposta, se foi uma resposta de sucesso, a Interface é atualizada com a nova tarefa, e habilita a opção para o usuário criar outra tarefa. E se o servidor retornar com uma resposta de erro, a interface se encarrega de exibir uma notificação de erro para o usuário. Com essa abordagem, garantimos uma Interface consistente com o servidor, entretanto o usuário precisa sempre esperar pela resposta do servidor para ver a tarefa na Interface ou para criar outra tarefa.

![Article-Todo-1](https://us1.discourse-cdn.com/elastic/original/3X/e/3/e3aca32d9c701659d941902f3fa8a28f3ec77140.gif)  
_Exemplo de uma lista de tarefas tradicional_

O código com React Query é simples e direto:

```ts
// Add a todo
const { mutate: createMutate } = useMutation(postAddTodo, {
  onError: (error: any, variables) => {
   // Show error notification 
    notifications.toasts.addDanger(
      `Error: ${error.message} when attempting to insert ${variables.description}`
    );
  },
  onSuccess: (res: any) => {
    // Refetch the todo list:
    queryClient.invalidateQueries('todos')
  },
});

```

Observe que não é uma experiência ruim, e se ambos a resposta do servidor e renderização da lista atualizad forem rápidas, o usuário não verá como um problema. Mas com aplicação de Optimistic UI, a experiéncia do usuário pode ser aprimorada.

## O Formato Otimístico

Numa Interface otimista, os usuários já vêem a tarefa criada assim que clicam no botão de adicionar, sem precisar esperar pela resposta do servidor, levando o usuário a ter uma experiência mais fluída:

![Article-Todo-Optimistic-1](https://us1.discourse-cdn.com/elastic/original/3X/0/c/0cfda719375dc5e68b8024b73f602713a0263817.gif)  
_Exemplo de lista de tarefas com Optimistic UI_

E o código, de acordo com a [documentação](https://react-query.tanstack.com/guides/optimistic-updates) do React Query, é aproximadamente assim:

```ts
const { mutate: createMutate } = useMutation(postAddTodo, {
  // When mutate is called:
  onMutate: async (todo) => {
    // Cancel any outgoing refetches (so they don't overwrite our optimistic update)
    await queryClient.cancelQueries('todos');

    // Snapshot the previous value
    const previousTodos = queryClient.getQueryData('todos');

    // Build a new todo object, only the necessary fields to show in the UI
    const newTodo: Todo = {
      _id: todo.id,
      _source: {
        description: todo.description,
        completed: false
      },
    };

    // Optimistically adding the new todo into the current todo list
    queryClient.setQueryData('todos', (previousData: any) => ({
      ...previousData,
      hits: [...previousData.hits, newTodo],
      total: {
        ...previousData.total,
        value: previousData.total.value + 1, // Incrementing the total count
      },
    }));

    // Return a context object with the snapshotted value
    return { previousTodos };
  },
  onError: (error: any, variables, context: any) => {
    // If the mutation fails, use the context returned from onMutate to roll back
    queryClient.setQueryData('todos', context.previousTodos);

    notifications.toasts.addDanger(
      `Error: ${error.message} when attempting to insert ${variables.description}`
    );
  },
  // Always refetch after error or success:
  onSettled: () => {
    queryClient.invalidateQueries('todos');
  }
});

```

Como vemos no código, se a requisição falhar, a ação que foi inserida de forma otimista, precisa ser revertida, para evitar que o usuário veja uma interface fora de sincronia com o servidor, ou seja, nesse exemplo sempre após a resposta do servidor a lista de tarefas é inteiramente sincronizada com o servidor, e temos uma interface sempre consistente.

![Article-Todo-Optimistic-Error-1](https://us1.discourse-cdn.com/elastic/original/3X/1/5/15b32cc3e76c7cd699e5b45e6bc7f5d333a5c37d.gif)  
_Exemplo de erro, o servidor foi configurado para retornar erro, sempre que a descrição for igual a "force error"_

## Ressalvas

Entretanto, há certos pontos para ficar atento, por exemplo, o que aconteceria se o usuário tentasse alterar a tarefa antes dela ser criada no servidor? Uma solução simples seria criar uma _flag_ para prevenir interações com a tarefa antes dela ser criada no servidor. Vale notar também, que em alguns casos, recarregar toda a lista de tarefas sempre que se cria uma tarefa pode tornar a experiência ruim, especialmente se a lista é muito grande para renderizar ou tem outros componentes que o usuário pode estar interagindo, e derepente a lista é atualizada. Em alguns casos, pode ser melhor da perspectiva do usuário, mostrar um _feedback_ visual de que a tarefa se encontra em um estado de erro, para que o usuário tente editar, remover ou enviar novamente.

Tudo isso pode ser controlado via código:

![Article-Todo-Optimistic-Error-2](https://us1.discourse-cdn.com/elastic/original/3X/1/0/10c13c3aea00597c92bc3bc9d79c92dbbaef3964.gif)

Como podemos ver, a experiência agora é ainda mais fluída para o usuário, mesmo na ocorrência de erros, porém isso vem ao custo de que adicionamos mais complexidade para mantermos em nosso projeto:

```ts
// Add a todo
  const { mutate: createMutate } = useMutation(postAddTodo, {
    // When mutate is called:
    onMutate: async (todo) => {
      // Cancel any outgoing refetches (so they don't overwrite our optimistic update)
      await queryClient.cancelQueries('todos');

      // Build a new todo object, only the necessary fields to show in the UI
      const newTodo: Todo = {
        _id: todo.id,
        _source: {
          description: todo.description,
          completed: false,
          placeholder: true, // This is a placeholder flag, we are using to prevent mutations while not synced with the server
          timestamp: new Date().toISOString(),
        },
      };

      // Optimistically adding the new todo into the current todo list
      queryClient.setQueryData('todos', (previousData: any) => ({
        ...previousData,
        hits: [...previousData.hits, newTodo],
        total: {
          ...previousData.total,
          value: previousData.total.value + 1, // Incrementing the total count
        },
      }));
    },
    onError: (error: any, variables) => {
      // Reverting the optimistic insert, removing the todo from the current todo list
      queryClient.setQueryData('todos', (previousData: any) => ({
        ...previousData,
        hits: previousData.hits.map((todo: any) => {
          if (todo._id === variables.id) {
            return {
              ...todo,
              _source: {
                ...todo._source,
                error: true, // Adding error flag, so the UI can show the todo in an error state
              },
            };
          }
          return todo;
        }),
        total: {
          ...previousData.total,
          value: previousData.total.value - 1, // Decrementing the total count
        },
      }));

      notifications.toasts.addDanger(
        `Error: ${error.message} when attempting to insert ${variables.description}`
      );
    },
    onSuccess: (res: any) => {
      // Optimistically sync the inserted todo with the server
      queryClient.setQueryData('todos', (previousData: any) => ({
        ...previousData,
        hits: previousData.hits.map((todo: any) => {
          // Change only the updated todo from the list with information from the server
          if (todo._id === res.todo.id) {
            return {
              ...todo,
              _source: {
                ...res.todo.body,
                placeholder: false, // Removing the placeholder flag, so the users can interact with the todo
              },
            };
          }
          return todo;
        }),
      }));
    },
  });

```

## Conclusão

Optimistic UI é uma técnica que pode ser utilizado para aprimorar a experiência do usuário, e fazer a interface parecer mais rápida, pois o usuário não precisa aguardar uma requisição terminar antes de executar outra tarefa. Entretanto, não significa que essa será sempre a melhor solução, pois tem casos onde é fundamental termos a resposta do servidor para mostrar ao usuário o próximo estágio (por exemplo uma ação de pagamento), mas vale a pena adicionar Optmistic UI como uma ferramenta a mais para o seu arsenal.

## Repositório

O código da lista de tarefas está disponível no Github como um _plugin_ para o [Kibana](https://www.elastic.co/kibana/), e as tarefas são gravadas no servidor em índices no [Elasticsearch](https://www.elastic.co/elasticsearch/), com exemplos de adicionar / remover e marcar uma tarefa como concluída.

> **[GitHub - opauloh/kibana\_optimistic\_todo\_list](https://github.com/opauloh/kibana_optimistic_todo_list)**
>
> Contribute to opauloh/kibana\_optimistic\_todo\_list development by creating an account on GitHub.

**Referências:**

- Documentação do React Query sobre [Optimistic updates](https://react-query.tanstack.com/guides/optimistic-updates)
- Este plugin foi criado utilizando o [Kibana Plugin Generator](https://www.elastic.co/guide/en/kibana/current/plugin-tooling.html)

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)\
**Post date:** [January 5, 2022, 8:00am UTC](https://discuss.elastic.co/t/dec-8th-2021-pt-br-interface-otimista-no-kibana-com-react-query/290812/2 "2022-01-05T08:00:19Z")

</div>

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.
