Практическое руководство. Использование функций аудитории в Fluid Framework

В этом руководстве описано, как использовать аудиторию Fluid Framework с React, чтобы создать визуальную демонстрацию пользователей, подключающихся к контейнеру. Объект аудитории содержит сведения, связанные со всеми пользователями, подключенными к контейнеру. В этом примере клиентская библиотека Azure будет использоваться для создания контейнера и аудитории.

На следующем рисунке показаны кнопки идентификатора и поле ввода идентификатора контейнера. Оставив поле идентификатора контейнера пустым и нажав кнопку идентификатора пользователя, создадит новый контейнер и присоединится к выбранному пользователю. Кроме того, конечный пользователь может ввести идентификатор контейнера и выбрать идентификатор пользователя для присоединения существующего контейнера в качестве выбранного пользователя.

A screenshot of a browser with buttons for selecting a user.

На следующем рисунке показано несколько пользователей, подключенных к контейнеру, представленному полями. Поле, очерченное синим цветом, представляет пользователя, который просматривает клиент, а поля, описанные в черном цвете, представляют других подключенных пользователей. При подключении новых пользователей к контейнеру с уникальным идентификатором число полей увеличится.

A screenshot of a browser showing information for four different container users.

Примечание.

В этом руководстве предполагается, что вы знакомы с обзором Fluid Framework и завершили краткое руководство. Вы также должны ознакомиться с основами React, созданием проектов React и React Hooks.

Создание проекта

  1. Откройте командную строку и перейдите в родительскую папку, в которой вы хотите создать проект; например, C:\My Fluid Projects.

  2. Выполните следующую команду в командной строке. (Обратите внимание, что интерфейс командной строки npx, а не npm. Он был установлен при установке Node.js.)

    npx create-react-app fluid-audience-tutorial
    
  3. Проект создается в подпапке с именем fluid-audience-tutorial. Перейдите к нему с помощью команды cd fluid-audience-tutorial.

  4. В проекте используются следующие библиотеки Fluid:

    Библиотека Description
    fluid-framework Содержит распределенную структуру данных SharedMap, которая синхронизирует данные между клиентами.
    @fluidframework/azure-client Определяет подключение к серверу службы "Жидкость" и определяет начальную схему для контейнера "Жидкость".
    @fluidframework/test-client-utils Определяет insecureTokenProvider , необходимый для создания подключения к текучим службам.

    Выполните следующую команду, чтобы установить библиотеки.

    npm install @fluidframework/azure-client @fluidframework/test-client-utils fluid-framework
    

Код проекта

Настройка переменных состояния и представления компонентов

  1. Откройте файл \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
        );
        }
    
  2. Замените 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]);
    
  3. Замените TODO 2 следующим кодом. Как указано выше, переменная определяет, отрисовывается ли компонент AudienceDisplay или компонент UserIdSelection.displayAudience Кроме того, функции для обновления переменных состояния передаются в компоненты в качестве свойств.

        (displayAudience) ?
        <AudienceDisplay userId={userId} containerId={containerId} onContainerNotFound={handleContainerNotFound}/> :
        <UserIdSelection onSelectUser={handleSelectUser}/>
    

Настройка компонента AudienceDisplay

  1. Создайте и откройте файл \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).

  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.

  1. Замените TODO 1 следующим кодом. Обратите внимание, что значения, которые userIduserNamecontainerId будут переданы из компонента приложения. Если нет 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 , когда компонент отображения аудитории подключен.

  1. Замените TODO 2 следующим кодом. Обратите внимание, что идентификатор пользователя будет поступать из родительского компонента как user1user2 или random. Если идентификатор используется randomMath.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();
    
  2. Замените 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) };
        });
        }, []);
    
  3. Замените TODO 4 следующим кодом. Обратите внимание, что если fluidMembers или currentMember он не инициализирован, отрисовывается пустой экран. Компонент AudienceList отрисовывает данные-члены с стили (для реализации в следующем разделе).

        if (!fluidMembers || !currentMember) return (<div/>);
    
        return (
            <AudienceList fluidMembers={fluidMembers} currentMember={currentMember}/>
        )
    

    Примечание.

    Подключение переходы могут привести к коротким окнам времени, где getMyself возвращаетсяundefined. Это связано с тем, что текущее подключение клиента еще не было добавлено в аудиторию, поэтому не удается найти соответствующий идентификатор подключения. Чтобы предотвратить отрисовку страницы Без участников аудитории, мы добавим прослушиватель для вызова updateMembersmembersChanged. Это работает, так как аудитория службы выдает membersChanged событие при подключении контейнера.

Создание представления

  1. Замените TODO 5 следующим кодом. Обратите внимание, что мы отрисовываем компонент списка для каждого члена, переданного из компонента AudienceDisplay . Для каждого элемента сначала сравниваются member.userId с currentMember.userId проверка, если этот элементisSelf. Таким образом, мы можем отличить пользователя клиента от других пользователей и отобразить компонент с другим цветом. Затем мы принудим компонент списка к массиву list . Каждый компонент будет отображать данные-члены, такие как userIduserName и 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>
            );
        });
    
  2. Замените TODO 6 следующим кодом. При этом будут отображены все элементы-члены, отправленные в list массив.

        return (
            <div>
                {list}
            </div>
        );
    

Настройка компонента UserIdSelection

  1. Создайте и откройте файл \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
        );
    }
    
  2. Замените 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);
        };
    
  3. Замените 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