Javascript — всё о localstorage и sessionstorage

HTML5-хранилище в действии

Давайте посмотрим на HTML5-хранилище в действии. Снова обратимся к игре «уголки», которую мы построили в главе про рисование. С этой игрой связана небольшая проблема: если вы закроете окно браузера посередине игры, то потеряете результаты. Но с HTML5-хранилищем мы можем сохранять процесс игры на месте, в самом браузере. Откройте демонстрацию, сделайте несколько ходов, закройте вкладку браузера, а затем снова ее откройте. Если ваш браузер поддерживает HTML5-хранилище, демонстрационная страница волшебным образом вспомнит точное положение в игре, в том числе, сколько ходов вы сделали, положение каждой фишки на доске и даже выбранную фишку.

Как это работает? Каждый раз, когда происходит изменение в игре, мы будем вызывать эту функцию.

function saveGameState() {
    if (!supportsLocalStorage()) { return false; }
    localStorage = gGameInProgress;
    for (var i = 0; i < kNumPieces; i++) {
        localStorage = gPieces.row;
        localStorage = gPieces.column;
    }
    localStorage = gSelectedPieceIndex;
    localStorage = gSelectedPieceHasMoved;
    localStorage = gMoveCount;
    return true;
}

Как видите, используется объект localStorage для сохранения процесса игры (gGameInProgress, логический тип). Далее перебираются все фишки (gPieces, массив JavaScript) и сохраняется строка и столбец для каждой из них. После чего сохраняются некоторые дополнительные состояния игры, включая выбранную фишку (gSelectedPieceIndex, целое число), фишку, которая находится в середине длинной серии прыжков (gSelectedPieceHasMoved, логический тип) и общее число сделанных ходов (gMoveCount, целое число).

При загрузке страницы вместо автоматического вызова функции newGame(), которая бы вернула все переменные в исходные значения, мы вызываем resumeGame(). Функция resumeGame() с помощью HTML5-хранилища проверяет состояние игры в локальном хранилище. Если оно есть, то восстанавливает значения с использованием объекта localStorage.

function resumeGame() {
    if (!supportsLocalStorage()) { return false; }
    gGameInProgress = (localStorage == «true»);
    if (!gGameInProgress) { return false; }
    gPieces = new Array(kNumPieces);
    for (var i = 0; i < kNumPieces; i++) {
        var row = parseInt(localStorage);
        var column = parseInt(localStorage);
        gPieces = new Cell(row, column);
    }
    gNumPieces = kNumPieces;
    gSelectedPieceIndex = parseInt(localStorage);
    gSelectedPieceHasMoved = localStorage == «true»;
    gMoveCount = parseInt(localStorage);
    drawBoard();
    return true;
}

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

К примеру, флаг о том, что игра в процессе (gGameInProgress) является логическим типом. В функции saveGameState() мы просто храним его и не беспокоимся о типе данных.

localStorage = gGameInProgress;

Но в функции resumeGame() мы должны рассмотреть значение, полученное из локального хранилища в виде строки и вручную построить собственное логическое значение.

gGameInProgress = (localStorage == «true»);

Аналогичным образом, число ходов хранится в gMoveCount как целое, в функции saveGameState() мы просто сохраняем его.

localStorage = gMoveCount;

Но в функции resumeGame() мы должны конвертировать значение в целое, используя встроенную в JavaScript функцию parseInt().

gMoveCount = parseInt(localStorage);

Usage

Write to Storage

This can be anywhere from within your application.

import React from 'react';
import { writeStorage } from '@rehooks/local-storage';

let counter = ;

const MyButton = () => (
  <button onClick={_ => writeStorage('i', ++counter)}>
    Click Me
  </button>
);

Read From Storage

This component will receive updates to itself from local storage.

Javascript:

import React from 'react';
import { useLocalStorage } from '@rehooks/local-storage';

function MyComponent() {
  const  = useLocalStorage('i'); // send the key to be tracked.
  return (
    <div>
      <h1>{counterValue}</h1>
    </div>
  );
}

Typescript:

import React from 'react';
import { useLocalStorage } from '@rehooks/local-storage';

function MyComponent() {
  const counterValue = useLocalStorage<number>('i'); // specify a type argument for your type
  // Note: Since there was no default value provided, this is potentially null.
  return (
    <div>
      <h1>{counterValue}</h1>
    </div>
  );
}

Optionally use a default value

import React from 'react';
import { useLocalStorage } from '@rehooks/local-storage';

function MyComponent() {
  // Note: The type of user can be inferred from the default value type
  const  = useLocalStorage('user', { name: 'Anakin Skywalker' });
  return (
    <div>
      <h1>{user.name}</h1>
    </div>
  );
}

Delete From Storage

You may also delete items from the local storage as well.

import { writeStorage, deleteFromStorage } from '@rehooks/local-storage';

writeStorage('name', 'Homer Simpson'); // Add an item first

deleteFromStorage('name'); // Deletes the item

const thisIsNull = localStorage.getItem('name'); // This is indeed null

Using With Context

It is advisable to use this hook with context if you want to have a properly
synchronized default value. Using in two different components
with the same key but different default values can lead to unexpected behaviour.

Using Context will also prevent components from rendering and setting
default values to the localStorage when you just want them to be deleted from localStorage
(assuming the context provider also does not re-render).

import React, { createContext, useContext } from 'react';
import { useLocalStorage } from '@rehooks/local-storage';

const defaultProfile = { name: 'Spongekebob' };
const defaultContextValue = ;

const ProfileContext = createContext(defaultContextValue);

export const ProfileProvider = ({ children }) => {
  const ctxValue = useLocalStorage('profile', defaultProfile);
  return (
    <ProfileContext.Provider value={ctxValue}>
      {children}
    </ProfileContext.Provider>
  );
};

const useProfile = () => useContext(ProfileContext);

const App = () => {
  const  = useProfile();
  return <h1>{profile && profile.name}</h1>;
};

export default () => {
  return (
    <ProfileProvider>
      <App />
    </ProfileProvider>
  );
};

Курсоры

Такие методы как возвращают массив ключей/значений.

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

Тогда метод вернёт ошибку при попытке получить все записи в массиве.

Что делать?

Курсоры предоставляют возможности для работы в таких ситуациях.

Объект cursor идёт по хранилищу объектов с заданным запросом (query) и возвращает пары ключ/значение по очереди, а не все сразу. Это позволяет экономить память.

Так как хранилище объектов внутренне отсортировано по ключу, курсор проходит по хранилищу в порядке хранения ключей (по возрастанию по умолчанию).

Синтаксис:

  • ключ или диапазон ключей, как для .
  • необязательный аргумент, доступные значения:

    • – по умолчанию, курсор будет проходить от самого маленького ключа к большему.
    • – обратный порядок: от самого большого ключа к меньшему.
    • , – то же самое, но курсор пропускает записи с тем же ключом, что уже был (только для курсоров по индексам, например, для нескольких книг с price=5, будет возвращена только первая).

Основным отличием курсора является то, что генерируется многократно: один раз для каждого результата.

Вот пример того, как использовать курсор:

Основные методы курсора:

  • – продвинуть курсор на позиций, пропустив значения.
  • – продвинуть курсор к следующему значению в диапазоне соответствия (или до позиции сразу после ключа key, если указан).

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

В приведённом выше примере курсор был создан для хранилища объектов.

Но мы также можем создать курсор для индексов. Как мы помним, индексы позволяют искать по полю объекта. Курсоры для индексов работают так же, как для хранилищ объектов – они позволяют экономить память, возвращая одно значение в единицу времени.

Для курсоров по индексам является ключом индекса (например price), нам следует использовать свойство как ключ объекта:

Веб-хранилище. Назначение localStorage и sessionStorage

Веб-хранилище — это данные, хранящиеся локально в браузере пользователя. Существует 2 типа веб-хранилищ:

  • LocalStorage;
  • SessionStorage.

В них вы можете хранить информацию в формате ключ-значение. Ключ и значение – это всегда строки.

Если мы попытаемся сохранить в значение элемента хранилища другой тип значений, например, объект, то он будет, перед тем как туда записан, преобразован в строку. В данном случае посредством неявного у него вызова метода . Т.е. в значении элемента этих хранилищ кроме строкового типа данных никакого другого содержаться не может.

Отличие между этими хранилищами сводится только к периоду времени, в течение которого они могут хранить данные, помещенные в них:

  • SessionStorage – выполняет это в течение определённого промежутка времени (сессии). Закрытие вкладки или браузера приводит их к удалению. При этом данные в SessionStorage сохраняются при обновлении страницы.
  • LocalStorage – осуществляет это в течение неограниченного времени. Они сохраняются при перезагрузке браузера и компьютера. Их длительность хранения ничем не ограничена. Но, хоть эти данные могут храниться бесконечно в браузере, обычный пользователь может их очень просто удалить, например выполнив очистку истории (при включенной опции «файлы cookie и другие данные сайтов»).

Хранилище LocalStorage похоже на cookies. Оно также применяется для хранения данных на компьютере пользователя (в браузере). Но кроме общих сходств имеется также и много отличий.

Cookies vs. LocalStorage: В чём разница?

Отличия между cookies и LocalStorage:

  • по месту хранения (куки и данные LocalStorage хранятся на компьютере пользователя в браузере);
  • по размеру (cookies ограничены 4 Кбайт, а размер LocalStorage — 5 Мбайт);
  • по включению этих данных в HTTP-заголовок (куки в отличие от данных локального хранилища включаются в состав запроса при отправке его на сервер, а также сервер их может добавлять в ответ при отправке его клиенту; таким образом cookies являются частью HTTP-протокола, и увеличивают объём передаваемых данных от клиента серверу и обратно);
  • по доступности данных (печеньки можно прочитать и установить как на сервере, так и на клиенте; на клиенте доступны все куки, кроме тех, у которых установлен флаг ; LocalStorage доступны только в браузере посредством JavaScript API);
  • по времени хранения данных (куки хранятся ограниченное время (до конца сеанса или истечения указанной даты), нахождение данных в локальном хранилище не ограничено по времени);
  • по удобству использования в JavaScript (работа с LocalStorage в JavaScript организовано намного удобнее чем с cookies);
  • по необходимости информирования пользователей Евросоюза (при использовании cookies сайт в ЕС должен получать на это разрешение от пользователей; для данных локального хранилища это не требуется);
  • по назначению (куки в основном используются для управления сеансом, персонализации и отслеживания действий пользователя, в то время как LocalStorage применяется в качестве обычного локального хранилища информации на компьютере пользователя).

Что использовать: LocalStorage или cookies? На самом деле, ответ на этот вопрос очень прост. Если вам не нужно отправлять данные с каждым HTTP-запросом на сервер, то в этом случае лучше использовать для хранения данных LocalStorage.

Безопасность данных

Хранилище LocalStorage привязана к источнику (домену, протоколу и порту). Данные, находящиеся в некотором источнике, доступны для всех сценариев страниц этого же источника. Из сценария, находящегося в одном источнике, нельзя получить доступ к данным, определяемым другим источником.

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

Итоги

Основные характеристики LocalStorage и SessionStorage:

  • данные хранятся в виде пар «ключ-значение»;
  • хранить можно только строки;
  • если вам необходимо хранить в этих хранилищах массивы и объекты, то сначала вы должны их превратить в строки, например, используя метод . Для преобразования строки обратно в массив или объект, можно использовать . Подробнее об этом позже.

The localStorage Object

The localStorage object stores the data with no expiration date. The data
will not be deleted when the browser is closed, and will be available the next day, week, or year.

Example

// StorelocalStorage.setItem(«lastname», «Smith»);// Retrieve
document.getElementById(«result»).innerHTML = localStorage.getItem(«lastname»);

Example explained:

  • Create a localStorage name/value pair with name=»lastname» and value=»Smith»
  • Retrieve the value of «lastname» and insert it into the element with id=»result»

The example above could also be written like this:

// StorelocalStorage.lastname = «Smith»;// Retrieve
document.getElementById(«result»).innerHTML = localStorage.lastname;

The syntax for removing the «lastname» localStorage item is as follows:

localStorage.removeItem(«lastname»);

Note: Name/value pairs are always stored as strings.
Remember to convert them to another format when needed!

The following example counts the number of times a user has clicked a button.
In this code the value string is converted to a number to be able to increase the counter:

Example

if (localStorage.clickcount) {  localStorage.clickcount = Number(localStorage.clickcount) + 1;
} else {  localStorage.clickcount = 1;}
document.getElementById(«result»).innerHTML = «You have clicked the button » +
localStorage.clickcount + » time(s).»;

Get Started

(1) You can install angular-local-storage using 3 different ways:Git:
clone & build this repositoryBower:

$ bower install angular-local-storage --save

npm:

$ npm install angular-local-storage

(2) Include (or ) from the dist directory in your , after including Angular itself.

(3) Add to your main module’s list of dependencies.

When you’re done, your setup should look similar to the following:

<!doctype html>
<html ng-app="myApp">
<head>

</head>
<body>
    ...
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script>
    <script src="bower_components/js/angular-local-storage.min.js"></script>
    ...
    <script>
        var myApp = angular.module('myApp', 'LocalStorageModule');

    </script>
    ...
</body>
</html>

Шаг 2: Получаем данные из localStorage

Всё логику получения данных мы положим в , но вы можете спокойно положить её и в конструктор, если конечно захотите, так как её первоочередная цель — это выставление изначального стейта нашему компоненту.

Обратите внимание, что вы не можете вызывать  перед тем, как компонент монтируется, так что, если вы предпочитаете подход с конструктором, то выставьте свойство стейта напрямую. Я же предпочитаю аккуратный подход к коснтуктору, настолько аккуратный насколько это возможно, но это дело предпочтений

componentDidMount() {
const rememberMe = localStorage.getItem(‘rememberMe’) === ‘true’;
const user = rememberMe ? localStorage.getItem(‘user’) : »;
this.setState({ user, rememberMe });
}

1
2
3
4
5

componentDidMount(){

constrememberMe=localStorage.getItem(‘rememberMe’)===’true’;

constuser=rememberMe?localStorage.getItem(‘user’)»;

this.setState({user,rememberMe});

}

Теперь давайте углубимся в код:

Первым делом мы получаем значение “Remember me”

Уже обратили внимание, что сравнение идёт со строкой “true”? Это потому что  хранит данные как строки. Таким образом нам надо получить хранящееся значение и запарсить его обратно в логический тип данных, перед его непосредственным использованием

Вы бы также могли использовать , если бы были точно уверены в том, что там всегда будет храниться булево значение в виде строки. Но сейчас это нам не подойдет, так как само значение не выставлено при инициализации страницы.

Далее мы получаем имя пользователя, но только в том случае, если  имеет значение .

И под конец мы назначаем эти значения стейту компонента.

И вот она магия!

И ещё совет

Как я говорил выше,  может хранить данные только в виде строк. Если вы имеете дело только с несколькими сохраненными значениями, то это не очень то и большая проблема. Но если вы хотите воспользоваться  на полную катушку в вашем приложении, то я очень советую взять на вооружение библиотеку, которая облегчит сохранение и получение данных, обойдя стороной такие процессы, как парсинг и обращение в строку. В этом деле идеально может подойти проверенная в боях Store.js. Это моя личная рекомендация.

Заключение

В React  очень просто использовать. Просто определите в каких случаях лучше сохранять и получать свои данные. Этот момент будет меняться от компонента к компоненту.

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

Если вам нужна хорошая библиотека для работы со значениями , то рассмотрите использование Store.js.

isSupported

Checks if the browser support the current storage type(e.g: , ).
Returns:

myApp.controller('MainCtrl', function($scope, localStorageService) {
  //...
  if(localStorageService.isSupported) {
    //...
  }
  //...
});

setPrefix

Change the local storage prefix during execution
Returns:

myApp.controller('MainCtrl', function($scope, localStorageService) {
  //...
  localStorageService.setPrefix('newPrefix');
  //...
});

getStorageType

Returns:

You can also dynamically change storage type by passing the storage type as the last parameter for any of the API calls. For example:

set

Directly adds a value to local storage.
If local storage is not supported, use cookies instead.Returns:

myApp.controller('MainCtrl', function($scope, localStorageService) {
  //...
  function submit(key, val) {
   return localStorageService.set(key, val);
  }
  //...
});

get

Directly get a value from local storage.
If local storage is not supported, use cookies instead.Returns:

myApp.controller('MainCtrl', function($scope, localStorageService) {
  //...
  function getItem(key) {
   return localStorageService.get(key);
  }
  //...
});

keys

Return array of keys for local storage, ignore keys that not owned.Returns:

myApp.controller('MainCtrl', function($scope, localStorageService) {
  //...
  var lsKeys = localStorageService.keys();
  //...
});

remove

Remove an item(s) from local storage by key.
If local storage is not supported, use cookies instead.Returns:

myApp.controller('MainCtrl', function($scope, localStorageService) {
  //...
  function removeItem(key) {
   return localStorageService.remove(key);
  }
  //...
  function removeItems(key1, key2, key3) {
   return localStorageService.remove(key1, key2, key3);
  }
});

clearAll

Remove all data for this app from local storage.
If local storage is not supported, use cookies instead.Note: Optionally takes a regular expression string and removes matching.Returns:

myApp.controller('MainCtrl', function($scope, localStorageService) {
  //...
  function clearNumbers(key) {
   return localStorageService.clearAll(/^\d+$/);
  }
  //...
  function clearAll() {
   return localStorageService.clearAll();
  }
});

bind

Bind $scope key to localStorageService.
Usage:
key: The corresponding key used in local storage
Returns: deregistration function for this listener.

myApp.controller('MainCtrl', function($scope, localStorageService) {
  //...
  localStorageService.set('property', 'oldValue');
  $scope.unbind = localStorageService.bind($scope, 'property');

  //Test Changes
  $scope.update = function(val) {
    $scope.property = val;
    $timeout(function() {
      alert("localStorage value: " + localStorageService.get('property'));
    });
  }
  //...
});
<div ng-controller="MainCtrl">
  <p>`property`</p>
  <input type="text" ng-model="lsValue"/>
  <button ng-click="update(lsValue)">update</button>
  <button ng-click="unbind()">unbind</button>
</div>

deriveKey

Return the derive key
Returns

myApp.controller('MainCtrl', function($scope, localStorageService) {
  //...
  localStorageService.set('property', 'oldValue');
  //Test Result
  console.log(localStorageService.deriveKey('property')); // ls.property
  //...
});

length

Return localStorageService.length, ignore keys that not owned.
Returns

myApp.controller('MainCtrl', function($scope, localStorageService) {
  //...
  var lsLength = localStorageService.length(); // e.g: 7
  //...
});

Транзакции

Термин «транзакция» является общеизвестным, транзакции используются во многих видах баз данных.

Транзакция – это группа операций, которые должны быть или все выполнены, или все не выполнены (всё или ничего).

Например, когда пользователь что-то покупает, нам нужно:

  1. Вычесть деньги с его счёта.
  2. Отправить ему покупку.

Будет очень плохо, если мы успеем завершить первую операцию, а затем что-то пойдёт не так, например отключат электричество, и мы не сможем завершить вторую операцию. Обе операции должны быть успешно завершены (покупка сделана, отлично!) или необходимо отменить обе операции (в этом случае пользователь сохранит свои деньги и может попытаться купить ещё раз).

Транзакции гарантируют это.

Все операции с данными в IndexedDB могут быть сделаны только внутри транзакций.

Для начала транзакции:

  • – это название хранилища, к которому транзакция получит доступ, например, . Может быть массивом названий, если нам нужно предоставить доступ к нескольким хранилищам.
  • – тип транзакции, один из:

    • – только чтение, по умолчанию.
    • – только чтение и запись данных, создание/удаление самих хранилищ объектов недоступно.

Есть ещё один тип транзакций: . Такие транзакции могут делать любые операции, но мы не можем создать их вручную. IndexedDB автоматически создаёт транзакцию типа , когда открывает базу данных, для обработчика . Вот почему это единственное место, где мы можем обновлять структуру базы данных, создавать/удалять хранилища объектов.

Почему существует несколько типов транзакций?

Производительность является причиной, почему транзакции необходимо помечать как или .

Несколько readonly транзакций могут одновременно работать с одним и тем же хранилищем объектов, а readwrite транзакций – не могут. Транзакции типа readwrite «блокируют» хранилище для записи. Следующая такая транзакция должна дождаться выполнения предыдущей, перед тем как получит доступ к тому же самому хранилищу.

После того, как транзакция будет создана, мы можем добавить элемент в хранилище, вот так:

Мы сделали четыре шага:

  1. Создать транзакцию и указать все хранилища, к которым необходим доступ, строка .
  2. Получить хранилище объектов, используя , строка .
  3. Выполнить запрос на добавление элемента в хранилище объектов , строка .
  4. …Обработать результат запроса , затем мы можем выполнить другие запросы и так далее.

Хранилища объектов поддерживают два метода для добавления значений:

  • put(value, )
    Добавляет значение в хранилище. Ключ необходимо указать, если при создании хранилища объектов не было указано свойство или . Если уже есть значение с таким же ключом, то оно будет заменено.

  • add(value, )
    То же, что , но если уже существует значение с таким ключом, то запрос не выполнится, будет сгенерирована ошибка с названием .

Аналогично открытию базы, мы отправляем запрос: и после ожидаем события .

  • для является ключом нового объекта.
  • Ошибка находится в (если есть).

Слежение за областью HTML5-хранилища

Если вы хотите программно отслеживать изменения хранилища, то должны отлавливать событие storage. Это событие возникает в объекте window, когда setItem(), removeItem() или clear() вызываются и что-то изменяют. Например, если вы установили существующее значение или вызвали clear() когда нет ключей, то событие не сработает, потому что область хранения на самом деле не изменилась.

Событие storage поддерживается везде, где работает объект localStorage, включая Internet Explorer 8. IE 8 не поддерживает стандарт W3C addEventListener (хотя он, наконец-то, будет добавлен в IE 9), поэтому, чтобы отловить событие storage нужно проверить, какой механизм событий поддерживает браузер (если вы уже проделывали это раньше с другими событиями, то можете пропустить этот раздел до конца). Перехват события storage работает так же, как и перехват других событий. Если вы предпочитаете использовать jQuery или какую-либо другую библиотеку JavaScript для регистрации обработчиков событий, то можете проделать это и со storage тоже.

if (window.addEventListener) {
  window.addEventListener(«storage», handle_storage, false);
} else {
  window.attachEvent(«onstorage», handle_storage);
};

Функция обратного вызова handle_storage будет вызвана с объектом StorageEvent, за исключением Internet Explorer, где события хранятся в window.event.

function handle_storage(e) {
  if (!e) { e = window.event; }
}

В данном случае переменная e будет объектом StorageEvent, который обладает следующими полезными свойствами.

Объект StorageEvent
Свойство Тип Описание
key string Ключ может быть добавлен, удален или изменен.
oldValue любой Предыдущее значение (если переписано) или null, если добавлено новое значение.
newValue любой Новое значение или null, если удалено.
url* string Страница, которая вызывает метод, приведший к изменению.

* Примечание: свойство url изначально называлось uri и некоторые браузеры поддерживали это свойство перед изменением спецификации. Для обеспечения максимальной совместимости вы должны проверить существует ли свойство url, и если нет проверить вместо него свойство uri.

Событие storage нельзя отменить, внутри функции обратного вызова handle_storage нет возможности остановить изменение. Это просто способ браузеру сказать вам: «Эй, это только что случилось. Вы ничего не можете сделать, я просто хотел, чтобы вы знали».

Используем localStorage в React

Перевод How to use localStorage with React

Как использовать преимущества localStorage в React? Да легко! В этой статье вы узнаете об удобстве применения локального хранилища в React приложениях.

Сердцем каждого приложения на React является его стейт. Компоненты могут иметь свой собственный внутренний стейт и поверьте мне, несмотря на всю их простоту, вы можете делать с ними много невероятно крутых вещей. Но давайте представим, что вы создаёте приложение и в какой-то момент вам надо локально сохранить данные от пользователя или даже сохранить весь стейт для одного или нескольких других компонентов. И тут, возможно, вам захочется понять, а как же можно использовать  в React приложении? Что же, начнем с того, что  тут вам уже не помощник, но не переживайте, все будет довольно просто. Читаем дальше.

Веб хранилище

Оно создано для того, чтобы хранить данные в браузере и оно потерпело значительное количество позитивных улучшений в сравнении со своим предшественником — кукисами. Сначала оно было как HTML5 API, ну а потом переросло уже в независимую рабочую единицу. Хранилище поддерживается буквально каждым современным браузером и даже некоторыми старичками, например такими как IE8. Веб хранилище реализовано двумя способами: первый — , постоянное хранилище данных, которое можно сравнить с постоянными кукисами, второе —, хранилище существующее только на время определенной сессии и на определенный период времени. Хоть эта статья и работает с , но вы также можете применить все описанные методики и на sessionStorage.

Наш компонент для рабочего примера

Итак, нам нужен компонент на котором мы будем все испытывать. Тут я думаю, что страница логина с опцией “Remember me” подойдет идеально. Для простоты и понятности, мы не будем добавлять поле пароля. Давайте посмотрим на метод :

render() {
return (
&lt;form onSubmit={this.handleFormSubmit}&gt;
&lt;label&gt;
User: &lt;input name=»user» value={this.state.user} onChange={this.handleChange}/&gt;
&lt;/label&gt;
&lt;label&gt;
&lt;input name=»rememberMe» checked={this.state.rememberMe} onChange={this.handleChange} type=»checkbox»/&gt; Remember me
&lt;/label&gt;
&lt;button type=»submit»&gt;Sign In&lt;/button&gt;
&lt;/form&gt;
);
}

1
2
3
4
5
6
7
8
9
10
11
12
13

render(){

return(

&lt;form onSubmit={this.handleFormSubmit}&gt;

&lt;label&gt;

User&lt;input name=»user»value={this.state.user}onChange={this.handleChange}&gt;

&lt;label&gt;

&lt;label&gt;

&lt;input name=»rememberMe»checked={this.state.rememberMe}onChange={this.handleChange}type=»checkbox»&gt;Remember me

&lt;label&gt;

&lt;button type=»submit»&gt;Sign In&lt;button&gt;

&lt;form&gt;

);

}

export default class SignIn extends Component {
state = {
user: »,
rememberMe: false
};

handleChange = (event) =&gt; {
const input = event.target;
const value = input.type === ‘checkbox’ ? input.checked : input.value;

this.setState({ : value });
};

handleFormSubmit = () =&gt; {};

render() { /*…*/ }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

exportdefaultclassSignInextendsComponent{

state={

user»,

rememberMefalse

};

handleChange=(event)=&gt;{

constinput=event.target;

constvalue=input.type===’checkbox’?input.checkedinput.value;

this.setState({input.namevalue});

};

handleFormSubmit=()=&gt;{};

render(){/*…*/}

}

Если вы заполните инпут для пользователя, а потом кликните на “Remember Me” чекбокс и под конец кликните на кнопку “Sign In”, то вы заметите, что форма снова опустеет:

Это вполне нормальное поведение. Теперь мы должны узнать как же нам использовать  в React и заставить форму работать должным образом.

Немного понятий и фактов

Web Storage (Веб-Хранилище) — это технология хранения данных на стороне клиента прямиком в браузере.

Два объекта — две идеи

Веб-хранилище подразделяется на два типа:

Local Storage

Локальное хранилище — это объект , используемый для хранения данных вида «Ключ — Значение» на постоянной основе. Иначе говоря, данные хранятся до тех пор, пока их принудительно не заставят уйти, то есть удалят.

Session Storage

Хранилище данных сеансов — это объект , используемый для хранения данных вида «Ключ — Значение» на временной основе. Данные хранятся до тех пор, пока не будет закрыта вкладка или окно браузера.

Что можно хранить и для чего использовать?

Хранить можно всё то, что сможет уложиться в строку, так как единственным разрешенным типом данных для хранения является текстовый тип — строка.

Навскидку, веб-хранилище можно забивать следующими данными:

  • Состояние работы приложения — Например, последний открытый документ, значение измененного поля ввода или история поиска по сайту.
  • Настройки пользователя — Например, выбранная тема оформления сайта или вид отображения информации.
  • Сохранение пользовательских данных при неудачной попытке отправки на сервер.
  • Запись данных по мере их ввода — Например, редактор текста StackEdit.

Поддержка браузеров

Картина замечательная, даже лучше, чем можно было представить себе:

Оценить всю сложившуюся на данный момент ситуацию можно на сайте .

Доступный размер

В отличии от печенек (HTTP cookie), веб-хранилище предоставляет намного больший размер — 4Кб против ~5Мб.

Проведя небольшой тест на реальных устройствах, получаем следующие данные:

  • Chrome 38, FireFox 33 и Opera 25 — размер хранилища 5000 Кб.
  • IE8-11 — размер хранилища 4750 Кб.
  • iOS 5-8.1 Safari, OS X Safari — размер хранилища 2500 Кб.
  • Android 4.x-5 — размер хранилища 5000 Кб.
  • Windows Phone 7.8 — размер хранилища 4750 Кб.

Устройства проверялись с «заводскими» настройками браузера, то есть никаких настроек до тестирования не проводилось. Протестировать ваше устройство можно используя вот «этот» скрипт.

Summary

Web storage objects and allow to store key/value in the browser.

  • Both and must be strings.
  • The limit is 5mb+, depends on the browser.
  • They do not expire.
  • The data is bound to the origin (domain/port/protocol).
Shared between all tabs and windows with the same origin Visible within a browser tab, including iframes from the same origin
Survives browser restart Survives page refresh (but not tab close)

API:

  • – store key/value pair.
  • – get the value by key.
  • – remove the key with its value.
  • – delete everything.
  • – get the key number .
  • – the number of stored items.
  • Use to get all keys.
  • We access keys as object properties, in that case event isn’t triggered.

Storage event:

  • Triggers on , , calls.
  • Contains all the data about the operation (), the document and the storage object .
  • Triggers on all objects that have access to the storage except the one that generated it (within a tab for , globally for ).

Итого

IndexedDB можно рассматривать как «localStorage на стероидах». Это простая база данных типа ключ-значение, достаточно мощная для оффлайн приложений, но простая в использовании.

Лучшим руководством является спецификация, текущая версия 2.0, но также поддерживаются несколько методов из 3.0 (не так много отличий) версии.

Использование можно описать в нескольких фразах:

  1. Подключить обёртку над промисами, например idb.
  2. Открыть базу данных:
    • Создание хранилищ объектов и индексов происходит в обработчике .
    • Обновление версии – либо сравнивая номера версий, либо можно проверить что существует, а что нет.
  3. Для запросов:
    • Создать транзакцию (можно указать readwrite, если надо).
    • Получить хранилище объектов .
  4. Затем для поиска по ключу вызываем методы непосредственно у хранилища объектов.
  5. Если данные не помещаются в памяти, то используйте курсор.

Демо-приложение:

Результат
index.html

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector