Skip to content

Instantly share code, notes, and snippets.

@rcapeto
Created May 22, 2023 20:08
Show Gist options
  • Save rcapeto/889d44fd329d253d9549107d9d35a2c6 to your computer and use it in GitHub Desktop.
Save rcapeto/889d44fd329d253d9549107d9d35a2c6 to your computer and use it in GitHub Desktop.
Documentação para o gerenciador de eventos

Controle de Eventos App Meutudo. 📱

  • Atualmente a meutudo possui vários mecanismos de disparo de evento e caso futuramente for implementado ou removido algum, nós podemos pensar em alguma maneira para separar esses disparos;
  • Pensando nisso, podemos implementar um gerenciador de eventos para ele ter controle dos eventos disparados e do listener ter a lógica do disparo de evento;

EventManager

  • Gerenciador;
  • Singleton, para nunca ser recriado;
  • Ele possui 3 características principais:
  1. Eventos
  2. Disparo
  3. Listeners

Estrutura do Manager

export class EventManager {
  private static INSTANCE: EventManager;
  private events: EventManagerObject;

  private constructor() {
    this.events = new Map();
  }

  public static getInstance() {
    if (!this.INSTANCE) {
      this.INSTANCE = new EventManager();
    }

    return this.INSTANCE;
  }

  public on(event: string, callback: EventManagerCallback): CallableFunction {
    const listeners = this.events.get(event);

    if (listeners) {
      listeners.add(callback);
    } else {
      this.events.set(event, new Set([callback]));
    }

    return () => {
      this.events.get(event)?.delete(callback);
    };
  }

  public emmit(event: string, params?: Partial<EventManagerCallbackParams>) {
    const listeners = this.events.get(event);

    if (listeners) {
      for (const callback of listeners.values()) {
        callback(params);
      }
    }
  }
}

Atributo Events

  • Onde vão estar os eventos, vamos pensar nesse Map como se fosse um objeto;

Método On

  • Método responsável por cadastrar os listeners dos eventos, então nela existe 2 parâmetros:
  1. Nome do evento, sempre sendo utilizado como enum;
  2. Funções que recebem os dados que foram enviados, seguindo uma tipagem.

Tipagem

enum EventType {
  INTERACTION = 'interaction',
}

interface EventManagerCallbackParams {
  eventValues?: Record<string, string>;
  eventName?: string;
  eventType?: EventType;
  eventScreenId?: string;
}

export type EventManagerCallback = (
  params?: Partial<EventManagerCallbackParams>,
) => void;

Exemplo de implementação:

enum Event {
  TEST = 'evento_teste'
}

function listener(params) {
  // Onde vou enviar os eventos para as plataformas
}

// Criar um LISTENER
manager.on(Event.TEST, callback)

Método emmit

  • Método responsável para disparar os eventos e com os listeners cadastrados vão receber esses eventos;
  • Esse método possúi 2 parâmetros:
  1. Nome do evento, sempre sendo utilizado como enum;
  2. Os dados que devem ser enviados.

Exemplo de implementação

const params = {
  eventName: 'evento_de_teste',
  eventType: EventType.INTERACTION,
  eventScreenId: 'tela_de_inicio',
};


manager.emmit(Event.TEST. params)

Estrutura Meutudo.

  • Podemos adicionar esse manager através de hook ou até mesmo de HOC;
  • Como o manager não depende de nenhuma alteração de estado ou algo parecido então o hook não é necessário, foi apenas escrito para facilicar o método;

HOC

Exemplo de implementação

import React, { FunctionComponent } from 'react';
import { useEventManager } from '~/events/manager/hooks/useEventManager';

export function withEventManager<Type extends object>() {
  return (Component: FunctionComponent<Type>) => {
    return (props: Type) => {
      const { manager } = useEventManager();
      return <Component {...props} eventManager={manager} />;
    };
  };
}

Hook

Exemplo de implementação

import { EventManager } from '~/events/manager/event-manager';

export function useEventManager() {
  const manager = EventManager.getInstance();
  return { manager };
}

Implementação em componentes

import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { useEventManager } from '~/events/manager/hooks/useEventManager';
import { ComponentWithEventManager } from '~/events/manager/types';
import { withEventManager } from '~/events/manager/hoc/withEventManager';
import { EventFGTS } from '~/events/manager/enums/fgts';

interface ComponentProps {}
type Props = ComponentWithEventManager<ComponentProps>;

function ExamplePage(props: Props) {
  const { manager } = useEventManager();

  function onButtonPress() {
    const eventName = 'fgts:start_integration';
    const eventScreenId = 'fgts_tela_de_integracao';
    const params = {
      eventName,
      eventScreenId,
    };

    manager.emmit(EventFGTS.START_INTEGRATION, params);
    props?.eventManager?.emmit(EventFGTS.START_INTEGRATION, params);
  }

  return (
    <View>
      <TouchableOpacity onPress={onButtonPress}>
        <Text>Iniciar integração</Text>
      </TouchableOpacity>
    </View>
  );
}

export default withEventManager<Props>()(ExamplePage);

Arquivo de tipagens:

import { EventManager } from '~/events/manager/event-manager';

export enum EventType {
  INTERACTION = 'interaction',
}

export interface EventManagerCallbackParams {
  eventValues?: Record<string, string>;
  eventName?: string;
  eventType?: EventType;
  eventScreenId?: string;
}

export type EventManagerCallback = (
  params?: Partial<EventManagerCallbackParams>,
) => void;
export type EventManagerObject = Map<string, Set<EventManagerCallback>>;

export type ComponentWithEventManager<Props = {}> = Props & {
  eventManager?: EventManager;
};

Cadastro dos listeners

  • Como na aplicação pode conter vários listeners e de vários contextos, podemos separa-los e criar um arquivo principal apenas para dar o start em todos:

Exemplo:

import { EventManager } from '~/events/manager/event-manager';
import { EventFGTS } from '~/events/manager/enums/fgts';

export function fgtsListener(manager: EventManager) {
  manager.on(EventFGTS.START_INTEGRATION, params => {
    // caso seja necessário apenas um emmit [sem parâmetros]
    if (params) {
      const { eventName, eventScreenId, eventType } = params;
    }
  });
}

//[index.ts]
import { EventManager } from '~/events/manager/event-manager';
import { fgtsListener } from '~/events/manager/listeners/fgts';

export function startAllListeners(manager: EventManager) {
  const listeners = [fgtsListener];

  for (const listener of listeners) {
    console.log(`[Event-Manager]: Start listener -> ${listener.name} with success!`);
    listener(manager);
  }
}

Estrutura de pastas [pensado]

-src
----events 
-------manager 
----------types.ts
----------event-manager.ts
----------listeners
-------------index 
-------------[nome-produto].ts
----------------index
-----------enums
--------------[nome-produto].ts
-----------hoc
--------------withEventManager
-----------hook
--------------useEventManager.ts

Considerações finais

  • Podemos pensar em implementar um tipo de repositório para apenas cadastrar e ele ter a inteligência de separar e disparar para cada: Exemplo:
function insiderRepository(params) {
 // disparar para a Insider
}

const eventManager = EventManager.getInstance([insiderRepository])

eventManager.on({{ event_name }}, ({ repositories, params }) => {
  for(const repository for repositories) {
    repository(params)
  }
})
  • Isso facilitaria no cadastro de novas plataformas e remoção de outras.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment