Практическое руководство. Использование функций аудитории в Fluid Framework
В этом руководстве описано, как использовать аудиторию Fluid Framework с React, чтобы создать визуальную демонстрацию пользователей, подключающихся к контейнеру. Объект аудитории содержит сведения, связанные со всеми пользователями, подключенными к контейнеру. В этом примере клиентская библиотека Azure будет использоваться для создания контейнера и аудитории.
На следующем рисунке показаны кнопки идентификатора и поле ввода идентификатора контейнера. Оставив поле идентификатора контейнера пустым и нажав кнопку идентификатора пользователя, создадит новый контейнер и присоединится к выбранному пользователю. Кроме того, конечный пользователь может ввести идентификатор контейнера и выбрать идентификатор пользователя для присоединения существующего контейнера в качестве выбранного пользователя.
На следующем рисунке показано несколько пользователей, подключенных к контейнеру, представленному полями. Поле, очерченное синим цветом, представляет пользователя, который просматривает клиент, а поля, описанные в черном цвете, представляют других подключенных пользователей. При подключении новых пользователей к контейнеру с уникальным идентификатором число полей увеличится.
Примечание.
В этом руководстве предполагается, что вы знакомы с обзором Fluid Framework и завершили краткое руководство. Вы также должны ознакомиться с основами React, созданием проектов React и React Hooks.
Создание проекта
Откройте командную строку и перейдите в родительскую папку, в которой вы хотите создать проект; например,
C:\My Fluid Projects
.Выполните следующую команду в командной строке. (Обратите внимание, что интерфейс командной строки npx, а не npm. Он был установлен при установке Node.js.)
npx create-react-app fluid-audience-tutorial
Проект создается в подпапке с именем
fluid-audience-tutorial
. Перейдите к нему с помощью командыcd fluid-audience-tutorial
.В проекте используются следующие библиотеки Fluid:
Библиотека Description fluid-framework
Содержит распределенную структуру данных SharedMap, которая синхронизирует данные между клиентами. @fluidframework/azure-client
Определяет подключение к серверу службы "Жидкость" и определяет начальную схему для контейнера "Жидкость". @fluidframework/test-client-utils
Определяет insecureTokenProvider , необходимый для создания подключения к текучим службам. Выполните следующую команду, чтобы установить библиотеки.
npm install @fluidframework/azure-client @fluidframework/test-client-utils fluid-framework
Код проекта
Настройка переменных состояния и представления компонентов
Откройте файл
\src\App.js
в редакторе кода. Удалите все инструкции по умолчаниюimport
. Затем удалите все разметку из инструкцииreturn
. Затем добавьте инструкции импорта для компонентов и перехватчиков React. Обратите внимание, что мы реализуем импортированные компоненты AudienceDisplay и UserIdSelection в последующих шагах. Файл должен выглядеть примерно так: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 ); }
Замените
TODO 1
следующим кодом. Этот код инициализирует переменные локального состояния, которые будут использоваться в приложении. ЗначениеdisplayAudience
определяет, отображается ли компонент AudienceDisplay или компонент UserIdSelection (см. ).TODO 2
ЗначениемuserId
является идентификатор пользователя для подключения к контейнеру иcontainerId
значением является контейнер для загрузки.handleContainerNotFound
ФункцииhandleSelectUser
передаются как обратные вызовы двум представлениям и управляют переходами состояния.handleSelectUser
вызывается при попытке создать или загрузить контейнер.handleContainerNotFound
вызывается при сбое создания или загрузки контейнера.Обратите внимание, что значения userId и containerId будут поступать из компонента UserIdSelection через функцию
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]);
Замените
TODO 2
следующим кодом. Как указано выше, переменная определяет, отрисовывается ли компонент AudienceDisplay или компонент UserIdSelection.displayAudience
Кроме того, функции для обновления переменных состояния передаются в компоненты в качестве свойств.(displayAudience) ? <AudienceDisplay userId={userId} containerId={containerId} onContainerNotFound={handleContainerNotFound}/> : <UserIdSelection onSelectUser={handleSelectUser}/>
Настройка компонента AudienceDisplay
Создайте и откройте файл
\src\AudienceDisplay.js
в редакторе кода. Добавьте следующие операторыimport
:import { useEffect, useState } from "react"; import { SharedMap } from "fluid-framework"; import { AzureClient } from "@fluidframework/azure-client"; import { InsecureTokenProvider } from "@fluidframework/test-client-utils";
Обратите внимание, что для определения пользователей и контейнеров требуются объекты, импортированные из библиотеки Fluid Framework. В следующих шагах AzureClient и InsecureTokenProvider будут использоваться для настройки клиентской службы (см. раздел
TODO 1
) в то время как SharedMap будет использоваться для настройки необходимогоcontainerSchema
для создания контейнера (см. разделTODO 2
).Добавьте следующие функциональные компоненты и вспомогательные функции:
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 }
Обратите внимание, что АудиторияDisplay и AudienceList являются функциональными компонентами, которые обрабатывают получение и отрисовку данных аудитории, а метод
tryGetAudienceObject
обрабатывает создание служб контейнера и аудитории.
Получение контейнера и аудитории
Вы можете использовать вспомогательная функция для получения данных "Жидкость" из объекта Аудитории в слой представления (состояние React). Метод tryGetAudienceObject
вызывается, когда компонент представления загружается после выбора идентификатора пользователя. Возвращаемое значение присваивается свойству состояния React.
Замените
TODO 1
следующим кодом. Обратите внимание, что значения, которыеuserId
userName
containerId
будут переданы из компонента приложения. Если нетcontainerId
, создается новый контейнер. Кроме того, обратите внимание, чтоcontainerId
он хранится в хэше URL-адреса. Пользователь, входящий в сеанс из нового браузера, может скопировать URL-адрес из существующего браузера сеансов или перейти иlocalhost:3000
вручную ввести идентификатор контейнера. С помощью этой реализации мы хотим упаковатьgetContainer
вызов в попытку перехвата в случае, если пользователь вводит идентификатор контейнера, который не существует. Дополнительные сведения см. в документации по контейнерам.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;
Получение аудитории при подключении компонентов
Теперь, когда мы определили, как получить аудиторию Fluid, необходимо сообщить React, чтобы вызвать tryGetAudienceObject
, когда компонент отображения аудитории подключен.
Замените
TODO 2
следующим кодом. Обратите внимание, что идентификатор пользователя будет поступать из родительского компонента какuser1
user2
илиrandom
. Если идентификатор используетсяrandom
Math.random()
для создания случайного числа в качестве идентификатора. Кроме того, имя будет сопоставлено пользователю на основе его идентификатора, как указано вuserNameList
. Наконец, мы определяем переменные состояния, которые будут хранить подключенные элементы, а также текущего пользователя.fluidMembers
будет хранить список всех элементов, подключенных к контейнеру, в то время какcurrentMember
будет содержать объект-член, представляющий текущего пользователя, просматривающего контекст браузера.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();
Замените
TODO 3
следующим кодом. При этом компонент будет вызыватьсяtryGetAudienceObject
, когда компонент подключен и заданы возвращаемые участникиfluidMembers
аудитории.currentMember
Обратите внимание, что мы проверяем, возвращается ли объект аудитории, если пользователь вводит идентификатор контейнера, который не существует, и нам нужно вернуть их в представление UserIdSelection (props.onContainerNotFound()
будет обрабатывать переключение представления). Кроме того, рекомендуется отменять регистрацию обработчиков событий при отключении компонента React, возвращаяaudience.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) }; }); }, []);
Замените
TODO 4
следующим кодом. Обратите внимание, что еслиfluidMembers
илиcurrentMember
он не инициализирован, отрисовывается пустой экран. Компонент AudienceList отрисовывает данные-члены с стили (для реализации в следующем разделе).if (!fluidMembers || !currentMember) return (<div/>); return ( <AudienceList fluidMembers={fluidMembers} currentMember={currentMember}/> )
Примечание.
Переходы подключений могут привести к коротким периодам времени, в которых
getMyself
возвращается.undefined
Это связано с тем, что текущее подключение клиента еще не было добавлено в аудиторию, поэтому не удается найти соответствующий идентификатор подключения. Чтобы предотвратить отрисовку страницы Без участников аудитории, мы добавим прослушиватель для вызоваupdateMembers
membersChanged
. Это работает, так как аудитория службы выдаетmembersChanged
событие при подключении контейнера.
Создание представления
Замените
TODO 5
следующим кодом. Обратите внимание, что мы отрисовываем компонент списка для каждого члена, переданного из компонента AudienceDisplay . Для каждого элемента сначала сравниваемmember.userId
currentMember.userId
, чтобы проверить, является ли этот элементisSelf
. Таким образом, мы можем отличить пользователя клиента от других пользователей и отобразить компонент с другим цветом. Затем мы принудим компонент списка к массивуlist
. Каждый компонент будет отображать данные-члены, такие какuserId
userName
иadditionalDetails
.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> ); });
Замените
TODO 6
следующим кодом. При этом будут отображены все элементы-члены, отправленные вlist
массив.return ( <div> {list} </div> );
Настройка компонента UserIdSelection
Создайте и откройте файл
\src\UserIdSelection.js
в редакторе кода. Этот компонент будет включать кнопки идентификатора пользователя и поля ввода идентификатора контейнера, которые позволяют конечным пользователям выбирать идентификатор пользователя и сеанс совместной работы. Добавьте следующиеimport
инструкции и функциональные компоненты:import { useState } from 'react'; export const UserIdSelection = (props) => { // TODO 1: Define styles and handle user inputs return ( // TODO 2: Return view components ); }
Замените
TODO 1
следующим кодом. Обратите внимание, чтоonSelectUser
функция обновит переменные состояния в родительском компоненте приложения и предложит изменить представление. МетодhandleSubmit
активируется элементами кнопки, которые будут реализованы вTODO 2
. Кроме того,handleChange
метод используется для обновления переменнойcontainerId
состояния. Этот метод будет вызываться из прослушивателя событий входного элемента, реализованного вTODO 2
. Кроме того, обратите внимание, что мы обновляемcontainerId
значение из HTML-элемента с идентификаторомcontainerIdInput
(определенным вTODO 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); };
Замените
TODO 2
следующим кодом. Это приведет к отображению кнопок идентификатора пользователя, а также поля ввода идентификатора контейнера.<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>
Запустите сервер "Жидкость" и запустите приложение
Примечание.
Чтобы соответствовать остальной части этого руководства, в этом разделе используются npx
и npm
команды для запуска сервера Fluid. Однако код, приведенный в этой статье, также может выполняться на сервере Ретранслятора Azure Fluid Relay. Дополнительные сведения см. в статье "Практическое руководство. Подготовка службы Azure Fluid Relay и практическое руководство. Подключение к службе Ретранслятора Жидкости Azure"
В командной строке выполните следующую команду, чтобы запустить службу "Жидкость".
npx @fluidframework/azure-local-service@latest
Откройте новую командную строку и перейдите в корневой каталог проекта; например, C:/My Fluid Projects/fluid-audience-tutorial
. Запустите сервер приложений с помощью следующей команды. Приложение откроется в браузере. Это может занять несколько минут.
npm run start
Перейдите на localhost:3000
вкладку браузера, чтобы просмотреть работающее приложение. Чтобы создать контейнер, нажмите кнопку идентификатора пользователя, оставив поле ввода идентификатора контейнера пустым. Чтобы имитировать нового пользователя, присоединенного к сеансу контейнера, откройте новую вкладку браузера и перейдите к ней localhost:3000
. На этот раз введите значение идентификатора контейнера, которое можно найти в url-адресе http://localhost:3000/#
первой вкладки браузера.
Примечание.
Возможно, потребуется установить дополнительную зависимость, чтобы сделать эту демонстрацию совместимой с Webpack 5. Если вы получаете ошибку компиляции, связанную с пакетом "buffer" или "url", выполните npm install -D buffer url
и повторите попытку. Это будет решено в будущем выпуске Fluid Framework.
Следующие шаги
- Попробуйте расширить демонстрацию с дополнительными парами "ключ-значение" в
additionalDetails
полеuserConfig
. - Рассмотрите возможность интеграции аудитории в приложение для совместной работы, которое использует распределенные структуры данных, такие как SharedMap или SharedString.
- Узнайте больше о аудитории.
Совет
При внесении изменений в код проект автоматически перестроится, а сервер приложений перезагрузит. Однако при внесении изменений в схему контейнера они вступают в силу только при закрытии и перезапуске сервера приложений. Для этого наведите фокус на командную строку и дважды нажмите клавиши CTRL-C. Затем снова запустите.npm run start