Uso de características de audiencia en Fluid Framework
En este tutorial, obtendrá información sobre el uso de la Audiencia de Fluid Framework con React para crear una demostración visual de usuarios que se conectan a un contenedor. El objeto de audiencia contiene información relacionada con todos los usuarios conectados al contenedor. En este ejemplo, se usará la biblioteca cliente de Azure para crear el contenedor y la audiencia.
En la imagen siguiente se muestran los botones de identificador y un campo de entrada de identificador de contenedor. Al dejar en blanco el campo de identificador de contenedor y hacer clic en un botón de identificador de usuario, se creará un contenedor y se unirá como el usuario seleccionado. Como alternativa, el usuario final puede escribir un identificador de contenedor y elegir un identificador de usuario para unirse a un contenedor existente como el usuario seleccionado.
En la imagen siguiente se muestran varios usuarios conectados a un contenedor representado por cuadros. El cuadro mostrado en azul representa al usuario que ve al cliente, mientras que los cuadros mostrados en negro representan a los demás usuarios conectados. A medida que los nuevos usuarios se conecten al contenedor con identificadores únicos, aumentará el número de cuadros.
Nota:
En este tutorial se da por supuesto que está familiarizado con la información general de Fluid Framework y que ha completado el Inicio rápido. También debe estar familiarizado con los conceptos básicos de React, creando proyectos de React y enlaces de React.
Creación del proyecto
Abra un símbolo del sistema y vaya a la carpeta principal donde desea crear el proyecto; por ejemplo,
C:\My Fluid Projects
.Ejecute el siguiente comando en el símbolo del sistema (tenga en cuenta que la CLI es npx, no npm. Se instaló cuando instaló Node.js).
npx create-react-app fluid-audience-tutorial
El proyecto se crea en una subcarpeta denominada
fluid-audience-tutorial
. Diríjase allí con el comandocd fluid-audience-tutorial
.El proyecto usa las siguientes bibliotecas de Fluid:
Biblioteca Descripción fluid-framework
Contiene la estructura de datos distribuidos SharedMap que sincroniza los datos entre los clientes. @fluidframework/azure-client
Define la conexión a un servidor del servicio Fluid y define el esquema de inicio para el contenedor de Fluid. @fluidframework/test-client-utils
Define el InsecureTokenProvider necesario para crear la conexión a un servicio Fluid. Ejecute el siguiente comando para instalar las bibliotecas.
npm install @fluidframework/azure-client @fluidframework/test-client-utils fluid-framework
Codificación del proyecto
Configuración de variables de estado y la vista de componentes
Abra el archivo
\src\App.js
en el editor de código. Elimine todas las instruccionesimport
predeterminadas. A continuación, elimine todo el marcado de la instrucciónreturn
. A continuación, agregue instrucciones de importación para componentes y enlaces de React. Tenga en cuenta que implementaremos los componentes AudienceDisplay y UserIdSelection importados en los pasos posteriores. El archivo debería tener este aspecto: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 ); }
Reemplaza
TODO 1
por el siguiente código. Este código inicializa las variables de estado locales que se usarán en la aplicación. El valordisplayAudience
determina si representamos el componente AudienceDisplay o el componente UserIdSelection (veaTODO 2
). El valoruserId
es el identificador de usuario con el que conectarse al contenedor y el valorcontainerId
es el contenedor que se va a cargar. Las funcioneshandleSelectUser
yhandleContainerNotFound
se pasan como devoluciones de llamada a las dos vistas y administran las transiciones de estado. Se llama ahandleSelectUser
al intentar crear o cargar un contenedor. Se llama ahandleContainerNotFound
cuando se produce un error al crear o cargar un contenedor.Tenga en cuenta que los valores userId y containerId procederán de un componente UserIdSelection a través de la función
handleSelectUser
.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]);
Reemplaza
TODO 2
por el siguiente código. Como se indicó anteriormente, la variabledisplayAudience
determinará si representamos el componente AudienceDisplay o el componente UserIdSelection. Además, las funciones para actualizar las variables de estado se pasan a componentes como propiedades.(displayAudience) ? <AudienceDisplay userId={userId} containerId={containerId} onContainerNotFound={handleContainerNotFound}/> : <UserIdSelection onSelectUser={handleSelectUser}/>
Configuración del componente AudienceDisplay
Cree y abra un archivo
\src\AudienceDisplay.js
en el editor de código. Agregue las instrucciones siguientesimport
:import { useEffect, useState } from "react"; import { SharedMap } from "fluid-framework"; import { AzureClient } from "@fluidframework/azure-client"; import { InsecureTokenProvider } from "@fluidframework/test-client-utils";
Tenga en cuenta que los objetos importados de la biblioteca de Fluid Framework son necesarios para definir usuarios y contenedores. En los pasos siguientes, AzureClient e InsecureTokenProvider se usarán para configurar el servicio de cliente (vea
TODO 1
), mientras que SharedMap se usará para configurar un elementocontainerSchema
necesario para crear un contenedor (veaTODO 2
).Agregue los siguientes componentes funcionales y funciones 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 }
Tenga en cuenta que AudienceDisplay y AudienceList son componentes funcionales que controlan la obtención y representación de datos de audiencia, mientras que el método
tryGetAudienceObject
controla la creación de servicios de contenedor y audiencia.
Obtención del contenedor y la audiencia
Puede usar una función auxiliar para obtener los datos de Fluid del objeto de audiencia en el nivel de vista (el estado React). Se llama al método tryGetAudienceObject
cuando se carga el componente de vista después de seleccionarse un identificador de usuario. El valor devuelto se asigna a una propiedad de estado React.
Reemplaza
TODO 1
por el siguiente código. Tenga en cuenta que los valores deuserId
userName
containerId
se pasarán desde el componente De aplicación. Si no hay ningúncontainerId
, se crea un nuevo contenedor. Además, tenga en cuenta quecontainerId
se almacena en el hash de la dirección URL. Un usuario que entra en una sesión de un nuevo explorador puede copiar la dirección URL de un explorador de sesión existente o navegar alocalhost:3000
y escribir manualmente el identificador de contenedor. Con esta implementación, queremos encapsular lagetContainer
llamada en un intento en caso de que el usuario escriba un identificador de contenedor que no exista. Visite la documentación de contenedores para obtener más información.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;
Obtención de la audiencia en el montaje de componentes
Ahora que hemos definido cómo obtener la audiencia de Fluid, es necesario indicar a React que llame a tryGetAudienceObject
cuando se monte el componente AudienceDisplay.
Reemplaza
TODO 2
por el siguiente código. Tenga en cuenta que el identificador de usuario provendrá del componente primario comouser1
user2
orandom
. Si el identificador esrandom
, usamosMath.random()
para generar un número aleatorio como identificador. Además, se asignará un nombre al usuario en función de su identificador, tal como se especifica enuserNameList
. Por último, definimos las variables de estado que almacenarán los miembros conectados, así como el usuario actual.fluidMembers
almacenará una lista de todos los miembros conectados al contenedor, mientras quecurrentMember
contendrá el objeto miembro que representa al usuario actual que ve el contexto del explorador.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();
Reemplaza
TODO 3
por el siguiente código. Este llamará atryGetAudienceObject
cuando se monte el componente y establezca los miembros de la audiencia devueltos enfluidMembers
ycurrentMember
. Tenga en cuenta que se comprueba si se devuelve un objeto de audiencia en caso de que un usuario escriba un containerId que no exista y necesitamos devolverlos a la vista UserIdSelection (props.onContainerNotFound()
controlará el cambio de la vista). Además, se recomienda anular el registro de los controladores de eventos cuando el componente React se desmonta devolviendoaudience.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) }; }); }, []);
Reemplaza
TODO 4
por el siguiente código. Tenga en cuenta que si no se han inicializadofluidMembers
nicurrentMember
, se representa una pantalla en blanco. El componente AudienceList representará los datos de miembros con la aplicación de estilos (que se implementarán en la sección siguiente).if (!fluidMembers || !currentMember) return (<div/>); return ( <AudienceList fluidMembers={fluidMembers} currentMember={currentMember}/> )
Nota:
Las transiciones de conexión pueden dar lugar a ventanas de tiempo breves en las que
getMyself
devuelveundefined
. Esto se debe a que la conexión de cliente actual aún no se habrá agregado a la audiencia, por lo que no se encuentra un identificador de conexión coincidente. Para evitar que React represente una página sin miembros de la audiencia, agregamos una escucha para llamar aupdateMembers
enmembersChanged
. Esto funciona porque la audiencia del servicio emite un eventomembersChanged
cuando el contenedor está conectado.
Crear la vista
Reemplaza
TODO 5
por el siguiente código. Tenga en cuenta que representamos un componente de lista para cada miembro pasado desde el componente AudienceDisplay. Para cada miembro, primero comparamosmember.userId
concurrentMember.userId
para comprobar si ese miembro esisSelf
. De este modo, podemos diferenciar al usuario del cliente de los demás usuarios y mostrar el componente con un color distinto. A continuación, insertamos el componente de lista en una matrizlist
. Cada componente mostrará datos de miembro comouserId
userName
yadditionalDetails
.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> ); });
Reemplaza
TODO 6
por el siguiente código. Esto representará todos y cada uno de los elementos de miembro insertados en la matrizlist
.return ( <div> {list} </div> );
Configuración del componente UserIdSelection
Cree y abra un archivo
\src\UserIdSelection.js
en el editor de código. Este componente incluirá botones de identificador de usuario y campos de entrada de identificador de contenedor que permiten a los usuarios finales elegir su identificador de usuario y sesión colaborativa. Agregue las instruccionesimport
y los componentes funcionales siguientes:import { useState } from 'react'; export const UserIdSelection = (props) => { // TODO 1: Define styles and handle user inputs return ( // TODO 2: Return view components ); }
Reemplaza
TODO 1
por el siguiente código. Tenga en cuenta que la funciónonSelectUser
actualizará las variables de estado en el componente App principal y solicitará un cambio de vista. El métodohandleSubmit
se desencadena mediante elementos de botón que se implementarán enTODO 2
. Además, el métodohandleChange
se usa para actualizar la variable de estadocontainerId
. Se llamará a este método desde una escucha de eventos de elementos de entrada implementada enTODO 2
. Además, tenga en cuenta que actualizamoscontainerId
obteniendo el valor de un elemento HTML con el identificadorcontainerIdInput
(definido enTODO 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); };
Reemplaza
TODO 2
por el siguiente código. Esto representará los botones de identificador de usuario, así como el campo de entrada de identificador de contenedor.<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>
Inicio del servidor de Fluid y ejecución de la aplicación
Nota:
A fin de coincidir con el resto de las instrucciones, en esta sección se usan los comandos npx
y npm
para iniciar un servidor de Fluid. Sin embargo, el código de este artículo también se puede ejecutar en un servidor de Azure Fluid Relay. Para obtener más información, consulte Procedimiento: Aprovisionamiento de un servicio Azure Fluid Realy y Procedimiento: Conexión a un servicio Azure Fluid Relay.
En el símbolo del sistema, ejecute el siguiente comando para iniciar el servicio Fluid.
npx @fluidframework/azure-local-service@latest
Abra un nuevo símbolo del sistema y vaya a la raíz del proyecto; por ejemplo, C:/My Fluid Projects/fluid-audience-tutorial
. Inicie el servidor de aplicaciones con el siguiente comando. La aplicación se abre en el explorador. Esta operación puede tardar unos minutos.
npm run start
Vaya a localhost:3000
en una pestaña de explorador para ver la aplicación en ejecución. Para crear un contenedor, seleccione un botón de identificador de usuario mientras deja en blanco la entrada de identificador de contenedor. Para simular que un nuevo usuario se una a la sesión de contenedor, abra una nueva pestaña de explorador y vaya a localhost:3000
. Esta vez, escriba el valor de identificador de contenedor que se puede encontrar en la dirección URL de la primera pestaña de explorador que sigue a http://localhost:3000/#
.
Nota:
Es posible que tenga que instalar una dependencia adicional para que esta demostración sea compatible con Webpack 5. Si recibe un error de compilación relacionado con un paquete "buffer" o "url", ejecute npm install -D buffer url
e inténtelo de nuevo. Esto se resolverá en una versión futura de Fluid Framework.
Pasos siguientes
- Intente ampliar la demostración con más pares clave-valor en el campo
additionalDetails
deuserConfig
. - Considere la posibilidad de integrar la audiencia en una aplicación colaborativa que utilice estructuras de datos distribuidas como SharedMap o SharedString.
- Obtenga más información sobre la audiencia.
Sugerencia
Cuando realice cambios en el código, el proyecto se recompilará automáticamente y el servidor de aplicaciones se volverá a cargar. Sin embargo, si realiza cambios en el esquema de contenedor, solo surtirán efecto si cierra y reinicia el servidor de aplicaciones. Para ello, ponga el enfoque en el símbolo del sistema y presione Ctrl-C dos veces. A continuación, vuelva a ejecutar npm run start
.