Procedura: Usare le funzionalità del gruppo di destinatari in Fluid Framework
In questa esercitazione si apprenderà come usare Fluid Framework Audience con React per creare una dimostrazione visiva degli utenti che si connettono a un contenitore. L'oggetto gruppo di destinatari contiene informazioni correlate a tutti gli utenti connessi al contenitore. In questo esempio verrà usata la libreria client di Azure per creare il contenitore e il gruppo di destinatari.
L'immagine seguente mostra i pulsanti ID e un campo di input DELL'ID contenitore. Lasciando vuoto il campo ID contenitore e facendo clic su un pulsante ID utente verrà creato un nuovo contenitore e aggiunto come utente selezionato. In alternativa, l'utente finale può immettere un ID contenitore e scegliere un ID utente per aggiungere un contenitore esistente come utente selezionato.
L'immagine successiva mostra più utenti connessi a un contenitore rappresentato da caselle. La casella evidenziata in blu rappresenta l'utente che visualizza il client mentre le caselle evidenziate in nero rappresentano gli altri utenti connessi. Man mano che i nuovi utenti si collegano al contenitore con ID univoco, il numero di caselle aumenterà.
Nota
Questa esercitazione presuppone che si abbia familiarità con La panoramica di Fluid Framework e che sia stata completata la guida introduttiva. È anche necessario avere familiarità con le nozioni di base di React, la creazione di progetti React e gli hook React.
Creare il progetto
Aprire un prompt dei comandi e passare alla cartella padre in cui si vuole creare il progetto; ad esempio .
C:\My Fluid Projects
Eseguire il comando seguente al prompt. Si noti che l'interfaccia della riga di comando è npx, non npm. È stato installato quando è stato installato Node.js.
npx create-react-app fluid-audience-tutorial
Il progetto viene creato in una sottocartella denominata
fluid-audience-tutorial
. Passare a esso con il comandocd fluid-audience-tutorial
.Il progetto usa le librerie Fluid seguenti:
Libreria Descrizione fluid-framework
Contiene la struttura di dati distribuiti SharedMap che sincronizza i dati tra i client. @fluidframework/azure-client
Definisce la connessione a un server di servizio Fluid e definisce lo schema iniziale per il contenitore Fluid. @fluidframework/test-client-utils
Definisce InsecureTokenProvider necessario per creare la connessione a un servizio fluido. Eseguire il comando seguente per installare le librerie.
npm install @fluidframework/azure-client @fluidframework/test-client-utils fluid-framework
Scrivere il codice del progetto
Configurare le variabili di stato e la visualizzazione dei componenti
Aprire il file
\src\App.js
nell'editor di codice. Eliminare tutte le istruzioni predefiniteimport
. Eliminare quindi tutto il markup dall'istruzionereturn
. Aggiungere quindi istruzioni di importazione per i componenti e gli hook React. Si noti che i componenti AudienceDisplay e UserIdSelection importati verranno eseguiti nei passaggi successivi. Il file dovrebbe avere un aspetto simile al seguente: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 ); }
Sostituisci
TODO 1
con il codice seguente. Questo codice inizializza le variabili di stato locale che verranno usate all'interno dell'applicazione. IldisplayAudience
valore determina se viene eseguito il rendering del componente AudienceDisplay o del componente UserIdSelection (vedereTODO 2
). IluserId
valore è l'identificatore utente con cui connettersi al contenitore e ilcontainerId
valore è il contenitore da caricare. LehandleSelectUser
funzioni ehandleContainerNotFound
vengono passate come callback alle due visualizzazioni e gestiscono le transizioni di stato.handleSelectUser
viene chiamato quando si tenta di creare/caricare un contenitore.handleContainerNotFound
viene chiamato quando la creazione/caricamento di un contenitore ha esito negativo.Si noti che i valori userId e containerId provengono da un componente UserIdSelection tramite la
handleSelectUser
funzione .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]);
Sostituisci
TODO 2
con il codice seguente. Come indicato in precedenza, ladisplayAudience
variabile determinerà se viene eseguito il rendering del componente AudienceDisplay o del componente UserIdSelection . Inoltre, le funzioni per aggiornare le variabili di stato vengono passate ai componenti come proprietà.(displayAudience) ? <AudienceDisplay userId={userId} containerId={containerId} onContainerNotFound={handleContainerNotFound}/> : <UserIdSelection onSelectUser={handleSelectUser}/>
Configurare il componente AudienceDisplay
Creare e aprire un file
\src\AudienceDisplay.js
nell'editor di codice. Aggiungere le istruzioniimport
seguenti:import { useEffect, useState } from "react"; import { SharedMap } from "fluid-framework"; import { AzureClient } from "@fluidframework/azure-client"; import { InsecureTokenProvider } from "@fluidframework/test-client-utils";
Si noti che gli oggetti importati dalla libreria Fluid Framework sono necessari per definire utenti e contenitori. Nei passaggi seguenti, AzureClient e InsecureTokenProvider verranno usati per configurare il servizio client (vedere
TODO 1
) mentre SharedMap verrà usato per configurare unacontainerSchema
necessità per creare un contenitore (vedereTODO 2
).Aggiungere i componenti funzionali e le funzioni helper seguenti:
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 }
Si noti che AudienceDisplay e AudienceList sono componenti funzionali che gestiscono il recupero e il rendering dei dati dei destinatari mentre il
tryGetAudienceObject
metodo gestisce la creazione di servizi contenitore e destinatari.
Ottenere contenitori e destinatari
È possibile usare una funzione helper per ottenere i dati Fluid, dall'oggetto Audience, al livello di visualizzazione (stato React). Il tryGetAudienceObject
metodo viene chiamato quando il componente di visualizzazione viene caricato dopo che è stato selezionato un ID utente. Il valore restituito viene assegnato a una proprietà di stato React.
Sostituisci
TODO 1
con il codice seguente. Si noti che i valori peruserId
userName
containerId
verranno passati dal componente App. Se noncontainerId
è presente , viene creato un nuovo contenitore. Si noti anche che l'oggettocontainerId
viene archiviato nell'hash url. Un utente che immette una sessione da un nuovo browser può copiare l'URL da un browser di sessione esistente oppure passare alocalhost:3000
e immettere manualmente l'ID contenitore. Con questa implementazione, si vuole eseguire il wrapping dellagetContainer
chiamata in un tentativo catch nel caso in cui l'utente inserisca un ID contenitore che non esiste. Per altre informazioni, vedere la documentazione relativa ai contenitori .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;
Ottenere il gruppo di destinatari sul montaggio dei componenti
Ora che è stato definito come ottenere il pubblico Fluido, è necessario indicare a React di chiamare tryGetAudienceObject
quando viene montato il componente Audience Display.
Sostituisci
TODO 2
con il codice seguente. Si noti che l'ID utente proviene dal componente padre comeuser1
user2
orandom
. Se l'ID vienerandom
usatoMath.random()
per generare un numero casuale come ID. Inoltre, verrà eseguito il mapping di un nome all'utente in base al relativo ID, come specificato inuserNameList
. Infine, definiamo le variabili di stato che archivieranno i membri connessi e l'utente corrente.fluidMembers
archivierà un elenco di tutti i membri connessi al contenitore, mentrecurrentMember
conterrà l'oggetto membro che rappresenta l'utente corrente che visualizza il contesto del browser.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();
Sostituisci
TODO 3
con il codice seguente. Verrà chiamato quandotryGetAudienceObject
il componente viene montato e impostare i membri del gruppo di destinatari restituiti sufluidMembers
ecurrentMember
. Si noti che viene verificato se viene restituito un oggetto audience nel caso in cui un utente inserisca un containerId che non esiste ed è necessario restituirli alla visualizzazione UserIdSelection (props.onContainerNotFound()
gestirà il cambio della visualizzazione). Inoltre, è consigliabile annullare la registrazione dei gestori eventi quando il componente React viene smontato restituendoaudience.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) }; }); }, []);
Sostituisci
TODO 4
con il codice seguente. Se ofluidMembers
currentMember
non è stato inizializzato, viene eseguito il rendering di una schermata vuota. Il componente AudienceList eseguirà il rendering dei dati del membro con stili (da implementare nella sezione successiva).if (!fluidMembers || !currentMember) return (<div/>); return ( <AudienceList fluidMembers={fluidMembers} currentMember={currentMember}/> )
Nota
Le transizioni di connessione possono comportare brevi finestre di intervallo in cui
getMyself
restituisceundefined
. Ciò è dovuto al fatto che la connessione client corrente non è stata ancora aggiunta al gruppo di destinatari, quindi non è possibile trovare un ID di connessione corrispondente. Per impedire a React di eseguire il rendering di una pagina senza membri del gruppo di destinatari, aggiungere un listener per chiamareupdateMembers
sumembersChanged
. Ciò funziona perché il gruppo di destinatari del servizio genera unmembersChanged
evento quando il contenitore è connesso.
Creare la visualizzazione
Sostituisci
TODO 5
con il codice seguente. Si noti che viene eseguito il rendering di un componente elenco per ogni membro passato dal componente AudienceDisplay . Per ogni membro, viene prima confrontatomember.userId
concurrentMember.userId
per verificare se tale membroisSelf
. In questo modo, è possibile distinguere l'utente client dagli altri utenti e visualizzare il componente con un colore diverso. Si esegue quindi il push del componente elenco in unalist
matrice. Ogni componente visualizzerà i dati dei membri,userId
userName
ad esempio 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> ); });
Sostituisci
TODO 6
con il codice seguente. Verrà eseguito il rendering di tutti gli elementi membro di cui è stato eseguito il push nellalist
matrice.return ( <div> {list} </div> );
Configurare il componente UserIdSelection
Creare e aprire un file
\src\UserIdSelection.js
nell'editor di codice. Questo componente includerà i pulsanti ID utente e i campi di input dell'ID contenitore che consentono agli utenti finali di scegliere l'ID utente e la sessione collaborativa. Aggiungere le istruzioni e i componenti funzionali seguentiimport
:import { useState } from 'react'; export const UserIdSelection = (props) => { // TODO 1: Define styles and handle user inputs return ( // TODO 2: Return view components ); }
Sostituisci
TODO 1
con il codice seguente. Si noti che laonSelectUser
funzione aggiornerà le variabili di stato nel componente app padre e richiederà una modifica della visualizzazione. IlhandleSubmit
metodo viene attivato dagli elementi pulsante che verranno implementati inTODO 2
. Inoltre, ilhandleChange
metodo viene usato per aggiornare lacontainerId
variabile di stato. Questo metodo verrà chiamato da un listener di eventi dell'elemento di input implementato inTODO 2
. Si noti anche che si aggiorna ilcontainerId
valore ottenuto da un elemento HTML con l'IDcontainerIdInput
(definito inTODO 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); };
Sostituisci
TODO 2
con il codice seguente. Verrà eseguito il rendering dei pulsanti ID utente e del campo di input dell'ID contenitore.<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>
Avviare il server Fluid ed eseguire l'applicazione
Nota
Per trovare la corrispondenza con il resto di questa procedura, questa sezione usa npx
i comandi e npm
per avviare un server Fluid. Tuttavia, il codice di questo articolo può essere eseguito anche su un server di inoltro fluido di Azure. Per altre informazioni, vedere Procedura: Effettuare il provisioning di un servizio Inoltro fluido di Azure e Procedura: Connettersi a un servizio di inoltro fluido di Azure
Nel prompt dei comandi eseguire il comando seguente per avviare il servizio Fluid.
npx @fluidframework/azure-local-service@latest
Aprire un nuovo prompt dei comandi e passare alla radice del progetto; ad esempio . C:/My Fluid Projects/fluid-audience-tutorial
Avviare il server applicazioni con il comando seguente. L'applicazione viene aperta nel browser. Il processo può richiedere alcuni minuti.
npm run start
Passare a localhost:3000
in una scheda del browser per visualizzare l'applicazione in esecuzione. Per creare un nuovo contenitore, selezionare un pulsante ID utente lasciando vuoto l'input dell'ID contenitore. Per simulare un nuovo utente che partecipa alla sessione del contenitore, aprire una nuova scheda del browser e passare a localhost:3000
. Questa volta, immettere il valore dell'ID contenitore che può essere trovato dall'URL della prima scheda del browser che procede http://localhost:3000/#
.
Nota
Potrebbe essere necessario installare una dipendenza aggiuntiva per rendere questa demo compatibile con Webpack 5. Se viene visualizzato un errore di compilazione correlato a un pacchetto "buffer" o "url", eseguire npm install -D buffer url
e riprovare. Questo problema verrà risolto in una versione futura di Fluid Framework.
Passaggi successivi
- Provare a estendere la demo con più coppie chiave/valore nel
additionalDetails
campo inuserConfig
. - Prendere in considerazione l'integrazione di gruppi di destinatari in un'applicazione collaborativa che usa strutture di dati distribuite, ad esempio SharedMap o SharedString.
- Altre informazioni sul gruppo di destinatari.
Suggerimento
Quando si apportano modifiche al codice, il progetto verrà ricompilato automaticamente e il server applicazioni verrà ricaricato. Tuttavia, se si apportano modifiche allo schema del contenitore, verranno applicate solo se si chiude e si riavvia il server applicazioni. A tale scopo, assegnare lo stato attivo al prompt dei comandi e premere CTRL+C due volte. Quindi eseguire npm run start
di nuovo.