Управление состоянием в Blazor Server. Использование хранилища браузера sessionStorage

При разработке приложений Blazor часто бывает необходимо сохранять какие-либо данные, введенные пользователем. Например, такое сохранение может потребоваться, если пользователь заполняет большую форму или добавляет в корзину какие-либо покупки. Если начнутся проблемы с соединением, например, у пользователя пропадет связь, то в итоге все введенные данные после обновления страницы приложения будут утеряны. Поэтому, для хранения временных данных, создаваемых пользователем, обычно используются коллекции браузера localStorage и sessionStorage .

Различия между localStorage и sessionStorage

  • localStorage ограничивается окном браузера. Так, если пользователь перезагружает страницу или закрывает и снова открывает браузер, то состояние сохраняется. Если пользователь открывает несколько вкладок браузера, это состояние совместно используется на нескольких вкладках. Данные сохраняются в localStorage до тех пор, пока они не будут явно очищены.
  • sessionStorage ограничивается вкладкой браузера. Если пользователь перезагружает вкладку, состояние сохраняется. Если пользователь закрывает вкладку или браузер, состояние теряется. Если пользователь открывает несколько вкладок браузера, каждая вкладка имеет собственную независимую версию данных.

Каким хранилищем браузера воспользоваться — решает разработчик, в зависимости от своих потребностей, но, как правило, чаще всего используется sessionStorage для хранение небольших по объему данных о состоянии приложения.

Пример использования sessionStorage в Blazor Server

Защищенное хранилище браузера ASP.NET Core, которое мы будем использовать, использует защиту данных ASP.NET Core для localStorage и sessionStorage и используется только для приложений Blazor Server.

В качестве примера возьмем демонстрационный компонент Counter и попробуем сохранить состояние счётчика в защищенном хранилище sessionStorage. Для того, чтобы воспользоваться хранилищем необходимо использовать в компоненте директиву Razor @inject следующим образом:

@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore

При этом, если предполагается, что в приложении хранилищем браузера будут пользоваться несколько компонентов, то директиву @using можно перенести в файл _Imports.razor, чтобы не подключать пространство имен каждый раз в каждом компоненте.

ProtectedSessionStorage использует асинхронные методы для сохранения или получения данных, поэтому перепишем код метода IncrementCount счётчика следующим образом:

Чтобы извлечь данные из хранилища, воспользуемся методом OnInitializedAsync компонента Blazor:

protected override async Task OnInitializedAsync()
{
    var result = await ProtectedSessionStore.GetAsync<int>("count");
    currentCount = result.Success ? result.Value : 0;
}

Здесь мы пытаемся прочитать значение count из хранилища и, если попытка увенчалась успехом (result.Success), то полю счётчика currentCount  присваивается полученное значение, иначе — присваивается ноль.

Теперь, чтобы наш пример заработал, необходимо отключить предварительную отрисовку компонентов на сервере. Для этого открываем файл _Host.cshtml и меняем строку:

<component type="typeof(App)" render-mode="ServerPrerendered" />

на

<component type="typeof(App)" render-mode="Server" />

Такая манипуляция потребовалась нам по следующим причине — во время предварительной отрисовки (ServerPrerendered):

  • интерактивное подключение к браузеру пользователя не существует;
  • в браузере еще нет страницы, на которой можно запустить код JavaScript.
  • localStorage или sessionStorage недоступны. Если компонент пытается взаимодействовать с хранилищем, выводится сообщение об ошибке, объясняющее, что вызовы взаимодействия с JavaScript осуществить невозможно, поскольку выполняется предварительная отрисовка компонента.

Теперь можно запустить приложение и убедиться, что состояние счётчика сохраняется даже если мы обновим страницу или перейдем на другой URL и, затем вернемся снова к счётчику. Чтобы убедиться, что данные действительно хранятся в браузере, можно открыть инструменты разработчика и посмотреть хранилища. Например, в Microsoft Edge это будет выглядеть следующим образом:

Так как мы используем именно защищенное хранилище ASP.NET Core, то, соответственно, ключ мы видим как есть, а значение ключа — зашифровано.

Хранение сложных объектов в браузере

Рассмотренный выше пример достаточно простой — храним значение int и, даже, если мы не получим во время значение счётчика, то он просто обнулится. Другой момент, когда мы храним в localStorage или sessionStorage, например, целые объекты. Так как операции чтения/записи в хранилище браузера осуществляются асинхронно, то на получение данных из хранилища требуется некоторое время и чем больший объем данных мы хотим получить, тем больше этот временной лаг. Если мы попытаемся вывести пользователю информацию об объекте, который мы ещё не успели получить из хранилища, то гарантированно получим ошибку.

Рассмотрим всё тот же пример со счётчиком, но представим, что его значение нам критически важно и, более того, значение счётчика может принимать значение null. Перепишем пример следующим образом:

@page "/counter"

@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

@if (currentCount.HasValue)
{
    <p role="status">Current count: @currentCount</p>

    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
}
else
{
<p>Загружаем данные....</p>
}


@code {
    private int? currentCount;

    private async Task IncrementCount()
    {
        currentCount++;
        await ProtectedSessionStore.SetAsync("counter", currentCount);
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
       if (firstRender)
       {
          await LoadStateAsync();
          StateHasChanged();
       }
    }

    private async Task LoadStateAsync()
    {
        var result = await ProtectedSessionStore.GetAsync<int>("counter");
        currentCount = result.Success ? result.Value : 0;
    }
}

Здесь, в разделе разметки мы проверяем, содержит ли currentCount какие-либо данные. Если нет, то пользователю вместо счётчика выводится строка «Загружаем данные….» до тех пор, пока данные не будут получены. Далее, в программной части компонента мы переопределили метод OnAfterRenderAsync. В этом методе мы проверяем, если компонент визуализируется впервые (а firstRender = true даже, если мы переходим на другие URL или обновляем страницу), то компонент пробует загрузить сведения из хранилища и оповещает компонент о смене состояния, что приводит к его перерисовке и отображению полученного из хранилища значения. На этом простом примере виден общий подход к работе со сложными объектами в хранилище браузера.

Итого

Чтобы воспользоваться хранилищем браузера в Blazor Server, мы должны обязательно отключить предварительную отрисовку (ServerPrerendered), а также использовать асинхронные методы чтения/записи. При этом, не рекомендуется хранить большие объемы данных в хранилище браузера, чтобы не снижать производительность приложения. Рекомендуемый объем хранимых в браузере данных не должен превышать нескольких килобайт.

Подписаться
Уведомить о
guest
3 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии