Front End

13 mai, 2024

Conheça o React Flow e adicione sincronização em tempo real no frontend

Publicidade

Hoje quero apresentar um novo framework para criar uma aplicação baseada em nós: o React Flow. É um framework JS para construir qualquer coisa, desde diagramas estáticos simples até visualizações de dados e editores visuais complexos. Neste artigo, vou mostrar como criar uma aplicação simples do React Flow, entender mais sobre nós e arestas, e adicionar sincronização em tempo real entre os participantes do mesmo conteúdo.

Instalação

Para começar com o React Flow, primeiro você precisará ter uma aplicação baseada em React e adicionar o pacote reactflow a ela, você pode usar qualquer gerenciador de pacotes

npm install reactflow

Nós e Arestas

Antes de começar, vou te familiarizar com o conceito de nó e aresta.

Em termos simples, os nós são os elementos ou objetos primários que você deseja representar em seu gráfico, e as arestas são as conexões ou relacionamentos entre esses nós. Por exemplo, em uma rede social, cada pessoa poderia ser um nó, e as amizades entre elas poderiam ser as arestas.

Tecnicamente falando, React Flow tem esses dois tipos: o Node, que conterá um ID, uma posição e seus dados, e o Edge que conterá um ID para ele, uma origem e um alvo. No código abaixo, criei uma lista de nós iniciais (alguns blocos) e uma lista de arestas iniciais: conectando o primeiro nó ao segundo nó.

export const initialNodes = [
	{
		id: '1',
		position: { x: 200, y: 200 },
		data: { label: 'First block' },
	},
	{ 
		id: '2', 
		position: { x: 250, y: 300 }, 
		data: { label: 'Second block' } 
	},
]

export const initialEdges = [ 
	{
		id: 'some-id', 
		source: '1', 
		target: '2' 
	}
]

Usando o React Flow

Depois de criar os nós e arestas iniciais, agora você está apto a adicionar o componente ReactFlow à sua aplicação. Como mostrado abaixo:

import React from 'react';
import ReactFlow from 'reactflow';
 
import 'reactflow/dist/style.css';
 
export default function App() {
  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <ReactFlow nodes={initialNodes} edges={initialEdges} />
    </div>
  );
}

No código acima, criamos um recipiente de tela cheia para nossos nodes. Também importamos os estilos de reactflow/dist/style.css.

Para facilitar a manipulação dos dados, vou usar os hooks [useNodesState] (https://www.notion.so/Dev-Relations-Rock-n-Roll-e403dc7c16ff4b4fba5f784cddd1747b?pvs=21) e [useEdgesState](<https://reactflow.dev/api-reference/hooks/use-edges-state>), que funcionam como o hook useState. Com adição a uma função de callback quando há alterações no estado.

import ReactFlow, { useNodesState, useEdgesState } from 'reactflow'

export default function App() {
	const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes)
	const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)

  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <ReactFlow nodes={nodes} edges={edges} />
    </div>
  );
}

Para criar uma experiência melhor para nossos usuários, vamos adicionar alguns controles, permitindo que eles ampliem e reduzam, para ver um mini mapa de seu conteúdo e outras funcionalidades. Vamos adicioná-lo como um filho ao nosso componente ReactFlow.

<ReactFlow nodes={nodes} edges={edges}>
	<Controls />
	<MiniMap />
</ReactFlow>

Para fazer um background, você também pode adicionar o elemento Background, no qual você poderá escolher variantes de fundo como pontos, cruzes ou linhas, bem como suas cores.

<Background variant={BackgroundVariant.Cross} color='#f00' />

Sincronizando entre usuários

Se você deseja criar um ambiente colaborativo onde vários usuários podem interagir com o React Flow ao mesmo tempo, você precisará usar o Real-time Data Engine. Este event broker permite que quaisquer alterações feitas por um usuário sejam instantaneamente visíveis para todos os outros usuários na mesma sala. Isso envolve a criação de uma sala única para cada grupo de usuários e a sincronização de suas atividades.

Criando uma sala compartilhada

Uma sala é um espaço virtual onde os usuários podem se juntar e colaborar. Ao criar uma sala, você pode especificar um ID único para ela.

Para criar uma sala, você precisa usar o SuperVizRoom que está disponível no pacote @superviz/sdk. Ao iniciar, você irá precisar de um token e um objeto de parâmetro. O objeto deve ter as seguintes propriedades:

  • id: O ID da sala, que deve ser uma string única compartilhada entre os participantes dessa sala.
  • participant: Um objeto que contém informações sobre o usuário atual, como name, id.
  • group: Um objeto que contém informações sobre o grupo ao qual o usuário pertence, como name e id.

Aqui está um exemplo de como criar uma sala com SuperViz:

// Import the SuperViz SDK
import SuperVizRoom from '@superviz/sdk';

// Create a room object
const room = await SuperVizRoom(DEVELOPER_KEY, {
  roomId: "<ROOM-ID>",
  participant: {
    id: "<USER-ID>",
    name: "<USER-NAME>"
  },
});

Adicionando Real-time Data Engine

Para adicionar o event broker, que será responsável por notificar quando uma mudança for feita e para escutar as mudanças de estado. Logo após criar uma sala, você pode adicionar o motor a ela.

const realtime = new Realtime();
room.addComponent(realtime);

Ouvindo as mudanças de estado

Para fazer uma sincronização completa, vamos ouvir alguns eventos: new-edge e node-drag, que ocorrem quando conectamos um nó React Flow a outro e quando arrastamos um nó pela tela.

Vamos primeiro criar uma função para lidar quando novas arestas são adicionadas, e então inscrevê-la no evento new-edge.

function onNewEdgesAdded({data, participantId}){
  // Verify if the origin of the event dispatch isn't the same participant on the page
  // preventing a infinity loop
	if (participantId === currentParticipantId) return;

  setEdges((eds) => addEdge(data.edge, eds));
}

realtime.subscribe('new-edge', onNewEdgesAdded)

Agora, faremos algo semelhante a ouvir o node-drag. Neste código, atualizaremos nossos nós com os dados, contendo sua nova posição, que recebemos do evento.

function onNodeDragEventHandler({data, participantId}){
	// Verify if the origin of the event dispatch isn't the same participant on the page
  // preventing a infinity loop
	if (participantId === currentParticipantId) return;

	setNodes((nodes: { id }[]) =>
		nodes.map((node: { id }) => (node.id === data.node.id ? data.node : node))
	);
}

realtime.subscribe('node-drag', onNodeDragEventHandler)

Publicando um evento

Agora que estamos ouvindo os eventos, precisamos acionar esses eventos, e neste caso usamos o método publish do objeto realtime. Sempre que um usuário adicionar uma nova aresta ou arrastar um nó, chamaremos realtime.publish, passando o nome do evento e os dados relevantes. Isso então acionará os ouvintes de evento correspondentes nas aplicações de outros usuários, mantendo todos os participantes sincronizados.

Para entender quando novas mudanças são feitas em ambos os cenários, precisamos usar as funções de callback do React Flow onConnect e onNodeDrag, como o código abaixo:

<ReactFlow
	...
	onNodeDrag={onNodeDrag}
	onConnect={onConnect}>
</ReactFlow>

A implementação do onNodeDrag é fácil, conforme mostrado abaixo:

function onNodeDrag(event, node) {
	realtime.publish('node-drag', { node });
}

Para publicar o evento new-edge em uma nova conexão (onConnect), é um pouco diferente, primeiro precisamos recriar o objeto de borda, adicioná-lo à nossa lista atual usando o método addEdge do React Flow, e então publicar no Real-time.

function onConnect(params) => {
	const edge = {
		...params,
		type: ConnectionLineType.SmoothStep,
		animated: true,
	}

	setEdges((edges) => addEdge(edge, edges))

	realtime.publish('new-edge', { edge })
}

Conclusão

Neste tutorial, te mostrei os conceitos básicos do React Flow e como criar um ambiente colaborativo em tempo real usando o Real-time Data Engine da SuperViz. Você pode ver este projeto em funcionamento na nossa página de demonstração dedicada ao React Flow.