Idb-keyval — это минималистичная библиотека для работы с
IndexedDB в браузере, предоставляющая простой интерфейс для сохранения и
получения данных. Одной из ключевых возможностей является метод
update, предназначенный для вложенного обновления
объектов, уже сохранённых в базе.
Синтаксис метода:
update(key, updater)
Метод возвращает промис, который разрешается после завершения
обновления. Если значение по ключу отсутствует, updater
получит undefined.
Пример базового использования:
import { update } from 'idb-keyval';
update('user', (val) => {
if (!val) return { name: 'Новый пользователь', settings: {} };
return { ...val, lastLogin: new Date() };
});
В этом примере обновляется поле lastLogin в существующем
объекте user, не затрагивая другие поля.
Часто объекты содержат вложенные структуры, например настройки пользователя:
{
name: 'Иван',
settings: {
theme: 'light',
notifications: { email: true, sms: false }
}
}
При обновлении таких объектов важно сохранять неизменяемость вложенных объектов, иначе возможна потеря данных или непредвиденное поведение.
Пример корректного вложенного обновления:
update('user', (val) => {
if (!val) return { name: 'Иван', settings: { theme: 'dark', notifications: {} } };
return {
...val,
settings: {
...val.settings,
notifications: {
...val.settings.notifications,
sms: true
}
}
};
});
Здесь мы обновляем только поле sms внутри
notifications, не затрагивая остальные настройки.
update с массивамиЕсли объект содержит массивы, их также можно обновлять вложенно.
Пример добавления элемента в массив tasks:
update('user', (val) => {
if (!val) return { name: 'Иван', tasks: [] };
return {
...val,
tasks: [...val.tasks, { id: 3, title: 'Новая задача', done: false }]
};
});
Использование оператора расширения ... гарантирует, что
существующие элементы массива не будут потеряны, а
новый объект корректно добавится.
При необходимости изменения нескольких уровней вложенности можно комбинировать несколько операций:
update('user', (val) => {
if (!val) return { name: 'Иван', settings: { theme: 'light', notifications: {} }, stats: { visits: 0 } };
return {
...val,
settings: {
...val.settings,
theme: 'dark',
notifications: {
...val.settings.notifications,
email: false
}
},
stats: {
...val.stats,
visits: val.stats.visits + 1
}
};
});
Этот подход позволяет обновлять сразу несколько вложенных объектов и массивов, сохраняя неизменяемость всей структуры.
update автоматически передаст undefined,
если ключ отсутствует в IndexedDB. Это позволяет
инициализировать новые записи одновременно с
обновлением:
update('cart', (val) => {
if (!val) return { items: [], total: 0 };
return {
...val,
items: [...val.items, { id: 5, name: 'Товар', price: 100 }],
total: val.total + 100
};
});
Таким образом, один метод update справляется и с
созданием, и с обновлением данных.
Функция updater может быть асинхронной.
update корректно обработает промис и сохранит
результат:
update('user', async (val) => {
const newSettings = await fetchUserSettingsFromServer();
return {
...val,
settings: { ...val?.settings, ...newSettings }
};
});
Это особенно полезно, когда новые значения нужно получить через API или из других асинхронных источников.
...
для сохранения существующих полей.undefined, чтобы
инициализировать новые записи корректно.push,
splice), вместо этого использовать map,
filter или оператор расширения.undefined).... для
сохранения неизменяемости.Такой подход делает update мощным инструментом для
работы с сложными объектами, обеспечивая безопасность
данных и простоту поддержки.