Como: Usar recursos de público no Fluid Framework
Neste tutorial, você aprenderá sobre como usar o Fluid Framework Audience com o React para criar uma demonstração visual dos usuários que se conectam a um contêiner. O objeto audience contém informações relacionadas a todos os usuários conectados ao contêiner. Neste exemplo, a biblioteca do Cliente do Azure será usada para criar o contêiner e a audiência.
A imagem a seguir mostra botões de ID e um campo de entrada de ID de contêiner. Deixar o campo ID do contêiner em branco e clicar em um botão de ID do usuário criará um novo contêiner e ingressará como o usuário selecionado. Como alternativa, o usuário final pode inserir um ID de contêiner e escolher um ID de usuário para ingressar em um contêiner existente como o usuário selecionado.
A próxima imagem mostra vários usuários conectados a um contêiner representado por caixas. A caixa delineada em azul representa o usuário que está visualizando o cliente, enquanto as caixas delineadas em preto representam os outros usuários conectados. À medida que novos usuários se conectam ao contêiner com IDs exclusivos, o número de caixas aumentará.
Nota
Este tutorial pressupõe que você esteja familiarizado com a Visão Geral do Fluid Framework e que tenha concluído o Guia de início rápido. Você também deve estar familiarizado com os conceitos básicos do React, da criação de projetos React e do React Hooks.
Criar o projeto
Abra um prompt de comando e navegue até a pasta pai onde você deseja criar o projeto; por exemplo,
C:\My Fluid Projects
.Execute o seguinte comando no prompt. (Observe que a CLI é npx, não npm. Ele foi instalado quando você instalou o Node.js.)
npx create-react-app fluid-audience-tutorial
O projeto é criado em uma subpasta chamada
fluid-audience-tutorial
. Navegue até ele com o comandocd fluid-audience-tutorial
.O projeto utiliza as seguintes bibliotecas Fluid:
Biblioteca Description fluid-framework
Contém a estrutura de dados distribuídos SharedMap que sincroniza dados entre clientes. @fluidframework/azure-client
Define a conexão com um servidor de serviço Fluid e define o esquema inicial para o contêiner Fluid. @fluidframework/test-client-utils
Define o InsecureTokenProvider necessário para criar a conexão com um Serviço Fluido. Execute o seguinte comando para instalar as bibliotecas.
npm install @fluidframework/azure-client @fluidframework/test-client-utils fluid-framework
Codifique o projeto
Configurar variáveis de estado e visualização de componentes
Abra o arquivo
\src\App.js
no editor de códigos. Exclua todas as instruções padrãoimport
. Em seguida, exclua toda a marcação dareturn
instrução. Em seguida, adicione instruções de importação para componentes e ganchos React. Observe que implementaremos os componentes importados AudienceDisplay e UserIdSelection nas etapas posteriores. O arquivo deve ter a seguinte aparência:import { useState, useCallback } from "react"; import { AudienceDisplay } from "./AudienceDisplay"; import { UserIdSelection } from "./UserIdSelection"; export const App = () => { // TODO 1: Define state variables to handle view changes and user input return ( // TODO 2: Return view components ); }
Substitua
TODO 1
pelo código abaixo. Esse código inicializa variáveis de estado local que serão usadas dentro do aplicativo. OdisplayAudience
valor determina se renderizamos o componente AudienceDisplay ou o componente UserIdSelection (consulteTODO 2
). OuserId
valor é o identificador de usuário com o qual se conectar ao contêiner e ocontainerId
valor é o contêiner a ser carregado. AshandleSelectUser
funções ehandleContainerNotFound
são passadas como retornos de chamada para as duas visualizações e gerenciam transições de estado.handleSelectUser
é chamado ao tentar criar/carregar um contêiner.handleContainerNotFound
é chamado quando a criação/carregamento de um contêiner falha.Observe que os valores userId e containerId virão de um componente UserIdSelection através da
handleSelectUser
função.const [displayAudience, setDisplayAudience] = useState(false); const [userId, setUserId] = useState(); const [containerId, setContainerId] = useState(); const handleSelectUser = useCallback((userId, containerId) => { setDisplayAudience(true) setUserId(userId); setContainerId(containerId); }, [displayAudience, userId, containerId]); const handleContainerNotFound = useCallback(() => { setDisplayAudience(false) }, [setDisplayAudience]);
Substitua
TODO 2
pelo código abaixo. Como dito acima, adisplayAudience
variável determinará se renderizamos o componente AudienceDisplay ou o componente UserIdSelection . Além disso, as funções para atualizar as variáveis de estado são passadas para componentes como propriedades.(displayAudience) ? <AudienceDisplay userId={userId} containerId={containerId} onContainerNotFound={handleContainerNotFound}/> : <UserIdSelection onSelectUser={handleSelectUser}/>
Configurar o componente AudienceDisplay
Crie e abra um arquivo
\src\AudienceDisplay.js
no editor de códigos. Adicione as seguintes instruçõesimport
:import { useEffect, useState } from "react"; import { SharedMap } from "fluid-framework"; import { AzureClient } from "@fluidframework/azure-client"; import { InsecureTokenProvider } from "@fluidframework/test-client-utils";
Observe que os objetos importados da biblioteca do Fluid Framework são necessários para definir usuários e contêineres. Nas etapas a seguir, AzureClient e InsecureTokenProvider serão usados para configurar o serviço do cliente (consulte
TODO 1
), enquanto o SharedMap será usado para configurar umcontainerSchema
necessário para criar um contêiner (consulteTODO 2
).Adicione os seguintes componentes funcionais e funções auxiliares:
const tryGetAudienceObject = async (userId, userName, containerId) => { // TODO 1: Create container and return audience object } export const AudienceDisplay = (props) => { //TODO 2: Configure user ID, user name, and state variables //TODO 3: Set state variables and set event listener on component mount //TODO 4: Return list view } const AudienceList = (data) => { //TODO 5: Append view elements to list array for each member //TODO 6: Return list of member elements }
Observe que AudienceDisplay e AudienceList são componentes funcionais que lidam com a obtenção e renderização de dados de audiência, enquanto o
tryGetAudienceObject
método lida com a criação de serviços de contêiner e audiência.
Obter contêiner e público
Você pode usar uma função auxiliar para obter os dados Fluid, do objeto Audience, para a camada de exibição (o estado React). O tryGetAudienceObject
método é chamado quando o componente de exibição é carregado depois que um ID de usuário é selecionado. O valor retornado é atribuído a uma propriedade de estado React.
Substitua
TODO 1
pelo código abaixo. Observe que os valores parauserId
userName
containerId
serão passados do componente Aplicativo . Se nãocontainerId
houver , um novo contêiner será criado. Além disso, observe que ocontainerId
é armazenado no hash da URL. Um usuário que entra em uma sessão de um novo navegador pode copiar a URL de um navegador de sessão existente ou navegar elocalhost:3000
inserir manualmente o ID do contêiner. Com essa implementação, queremos envolver agetContainer
chamada em uma tentativa de captura no caso de o usuário inserir um ID de contêiner que não existe. Visite a documentação de contêineres para obter mais informações.const userConfig = { id: userId, name: userName, additionalDetails: { email: userName.replace(/\s/g, "") + "@example.com", date: new Date().toLocaleDateString("en-US"), }, }; const serviceConfig = { connection: { type: "local", tokenProvider: new InsecureTokenProvider("", userConfig), endpoint: "http://localhost:7070", }, }; const client = new AzureClient(serviceConfig); const containerSchema = { initialObjects: { myMap: SharedMap }, }; let container; let services; if (!containerId) { ({ container, services } = await client.createContainer(containerSchema)); const id = await container.attach(); location.hash = id; } else { try { ({ container, services } = await client.getContainer(containerId, containerSchema)); } catch (e) { return; } } return services.audience;
Colocar o público na montagem de componentes
Agora que definimos como obter o público fluido, precisamos dizer ao React para ligar tryGetAudienceObject
quando o componente Exibição de público estiver montado.
Substitua
TODO 2
pelo código abaixo. Observe que o ID do usuário virá do componente pai como um ouuser1
user2
random
. Se o ID érandom
que usamosMath.random()
para gerar um número aleatório como o ID. Além disso, um nome será mapeado para o usuário com base em seu ID, conforme especificado emuserNameList
. Por fim, definimos as variáveis de estado que armazenarão os membros conectados, bem como o usuário atual.fluidMembers
armazenará uma lista de todos os membros conectados ao contêiner, enquantocurrentMember
conterá o objeto member que representa o usuário atual visualizando o contexto do navegador.const userId = props.userId == "random" ? Math.random() : props.userId; const userNameList = { "user1" : "User One", "user2" : "User Two", "random" : "Random User" }; const userName = userNameList[props.userId]; const [fluidMembers, setFluidMembers] = useState(); const [currentMember, setCurrentMember] = useState();
Substitua
TODO 3
pelo código abaixo. Isso chamará otryGetAudienceObject
quando o componente for montado e definirá os membros da audiência retornados comofluidMembers
ecurrentMember
. Nota, verificamos se um objeto audience é retornado no caso de um usuário inserir um containerId que não existe e precisamos retorná-los para a visualização UserIdSelection (props.onContainerNotFound()
tratará de alternar a exibição). Além disso, é uma boa prática cancelar o registro de manipuladores de eventos quando o componente React é desmontado retornandoaudience.off
.useEffect(() => { tryGetAudienceObject(userId, userName, props.containerId).then(audience => { if(!audience) { props.onContainerNotFound(); alert("error: container id not found."); return; } const updateMembers = () => { setFluidMembers(audience.getMembers()); setCurrentMember(audience.getMyself()); } updateMembers(); audience.on("membersChanged", updateMembers); return () => { audience.off("membersChanged", updateMembers) }; }); }, []);
Substitua
TODO 4
pelo código abaixo. Observe que, se ofluidMembers
oucurrentMember
não tiver sido inicializado, uma tela em branco será renderizada. O componente AudienceList renderizará os dados do membro com estilo (a ser implementado na próxima seção).if (!fluidMembers || !currentMember) return (<div/>); return ( <AudienceList fluidMembers={fluidMembers} currentMember={currentMember}/> )
Nota
Transições de conexão podem resultar em janelas de tempo curtas onde
getMyself
retornaundefined
. Isso ocorre porque a conexão do cliente atual ainda não terá sido adicionada ao público, portanto, um ID de conexão correspondente não pode ser encontrado. Para impedir que o React renderize uma página sem membros do público, adicionamos um ouvinte para chamarupdateMembers
membersChanged
o . Isso funciona porque o público do serviço emite ummembersChanged
evento quando o contêiner está conectado.
Criar a vista
Substitua
TODO 5
pelo código abaixo. Observe que estamos renderizando um componente de lista para cada membro passado do componente AudienceDisplay . Para cada membro, primeiro comparamosmember.userId
paracurrentMember.userId
verificar se esse membroisSelf
. Desta forma, podemos diferenciar o usuário cliente dos outros usuários e exibir o componente com uma cor diferente. Em seguida, enviamos o componente de lista para umalist
matriz. Cada componente exibirá dados de membros, comouserId
userName
eadditionalDetails
.const currentMember = data.currentMember; const fluidMembers = data.fluidMembers; const list = []; fluidMembers.forEach((member, key) => { const isSelf = (member.userId === currentMember.userId); const outlineColor = isSelf ? 'blue' : 'black'; list.push( <div style={{ padding: '1rem', margin: '1rem', display: 'flex', outline: 'solid', flexDirection: 'column', maxWidth: '25%', outlineColor }} key={key}> <div style={{fontWeight: 'bold'}}>Name</div> <div> {member.userName} </div> <div style={{fontWeight: 'bold'}}>ID</div> <div> {member.userId} </div> <div style={{fontWeight: 'bold'}}>Connections</div> { member.connections.map((data, key) => { return (<div key={key}>{data.id}</div>); }) } <div style={{fontWeight: 'bold'}}>Additional Details</div> { JSON.stringify(member.additionalDetails, null, '\t') } </div> ); });
Substitua
TODO 6
pelo código abaixo. Isso renderizará todos os elementos membros que enviamos para alist
matriz.return ( <div> {list} </div> );
Configurar componente UserIdSelection
Crie e abra um arquivo
\src\UserIdSelection.js
no editor de códigos. Este componente incluirá botões de ID de utilizador e campos de entrada de ID de contentor que permitem aos utilizadores finais escolher o seu ID de utilizador e sessão colaborativa. Adicione as seguintesimport
instruções e componentes funcionais:import { useState } from 'react'; export const UserIdSelection = (props) => { // TODO 1: Define styles and handle user inputs return ( // TODO 2: Return view components ); }
Substitua
TODO 1
pelo código abaixo. Observe que aonSelectUser
função atualizará as variáveis de estado no componente App pai e solicitará uma alteração de exibição. OhandleSubmit
método é acionado por elementos de botão que serão implementados emTODO 2
. Além disso, ohandleChange
método é usado para atualizar acontainerId
variável de estado. Esse método será chamado a partir de um ouvinte de eventos do elemento de entrada implementado noTODO 2
. Além disso, observe que atualizamos ocontainerId
be obtendo o valor de um elemento HTML com o idcontainerIdInput
(definido emTODO 2
).const selectionStyle = { marginTop: '2rem', marginRight: '2rem', width: '150px', height: '30px', }; const [containerId, setContainerId] = (location.hash.substring(1)); const handleSubmit = (userId) => { props.onSelectUser(userId, containerId); } const handleChange = () => { setContainerId(document.getElementById("containerIdInput").value); };
Substitua
TODO 2
pelo código abaixo. Isso renderizará os botões de ID do usuário, bem como o campo de entrada de ID do contêiner.<div style={{display: 'flex', flexDirection:'column'}}> <div style={{marginBottom: '2rem'}}> Enter Container Id: <input type="text" id="containerIdInput" value={containerId} onChange={() => handleChange()} style={{marginLeft: '2rem'}}></input> </div> { (containerId) ? (<div style={{}}>Select a User to join container ID: {containerId} as the user</div>) : (<div style={{}}>Select a User to create a new container and join as the selected user</div>) } <nav> <button type="submit" style={selectionStyle} onClick={() => handleSubmit("user1")}>User 1</button> <button type="submit" style={selectionStyle} onClick={() => handleSubmit("user2")}>User 2</button> <button type="submit" style={selectionStyle} onClick={() => handleSubmit("random")}>Random User</button> </nav> </div>
Inicie o servidor Fluid e execute o aplicativo
Nota
Para corresponder ao resto deste tutorial, esta seção usa npx
e npm
comandos para iniciar um servidor Fluid. No entanto, o código neste artigo também pode ser executado em um servidor Azure Fluid Relay. Para obter mais informações, consulte Como provisionar um serviço do Azure Fluid Relay e Como conectar-se a um serviço do Azure Fluid Relay
No prompt de comando, execute o seguinte comando para iniciar o serviço Fluid.
npx @fluidframework/azure-local-service@latest
Abra um novo prompt de comando e navegue até a raiz do projeto; por exemplo, C:/My Fluid Projects/fluid-audience-tutorial
. Inicie o servidor de aplicativos com o seguinte comando. O aplicativo é aberto no navegador. Esta operação poderá demorar alguns minutos.
npm run start
Navegue até uma localhost:3000
guia do navegador para visualizar o aplicativo em execução. Para criar um novo contêiner, selecione um botão de ID de usuário deixando a entrada de ID de contêiner em branco. Para simular um novo usuário ingressando na sessão de contêiner, abra uma nova guia do navegador e navegue até localhost:3000
. Desta vez, insira o valor de ID do contêiner que pode ser encontrado na url http://localhost:3000/#
da primeira guia do navegador.
Nota
Pode ser necessário instalar uma dependência adicional para tornar esta demonstração compatível com o webpack 5. Se você receber um erro de compilação relacionado a um pacote "buffer" ou "url", execute npm install -D buffer url
e tente novamente. Isso será resolvido em uma versão futura do Fluid Framework.
Próximos passos
- Tente estender a demonstração com mais pares chave/valor no
additionalDetails
campo emuserConfig
. - Considere integrar o público em um aplicativo colaborativo que utiliza estruturas de dados distribuídas, como SharedMap ou SharedString.
- Saiba mais sobre o Público-alvo.
Gorjeta
Quando você faz alterações no código, o projeto será reconstruído automaticamente e o servidor de aplicativos será recarregado. No entanto, se você fizer alterações no esquema de contêiner, elas só terão efeito se você fechar e reiniciar o servidor de aplicativos. Para fazer isso, dê foco ao prompt de comando e pressione Ctrl-C duas vezes. Em seguida, execute npm run start
novamente.