Ми регулярно отримуємо запити на реалізацію спільного редагування таблиць у мобільному додатку. Таблиця — не текст. У неї є структура: рядки, стовпці, комірки, формули, залежності між комірками. Алгоритми синхронізації, які працюють для текстових документів (Y.js <cite>YText</cite>, CRDT для послідовностей), тут застосовуються інакше. Конкурентна зміна комірки B3 та одночасна зміна формули =SUM(B1:B5) в C1 — це дві незалежні події із залежністю за даними. Ми пропонуємо рішення під ключ із використанням CRDT.
Спільне редагування таблиць: як забезпечити узгодженість даних?
Модель даних: не масив рядків, а Map комірок
Таблиця в CRDT-представленні — це Y.Map (або аналог), де ключ — "row:col" рядок, значення — об'єкт комірки. Y.js YMap підтримує конкурентні операції на рівні окремих ключів: два користувачі редагують різні комірки одночасно — немає конфлікту. Два редагують одну комірку — перемагає останній запис (Last-Write-Wins семантика). Це підтверджує документація Y.js.
const ycells = ydoc.getMap('cells'); ycells.set('3:2', { value: '=SUM(A1:A3)', formatted: '15', style: { bold: true } }); ycells.observe(event => { event.changes.keys.forEach((change, key) => { if (change.action === 'update' || change.action === 'add') { const [row, col] = key.split(':').map(Number); recalculateAffectedFormulas(row, col); rerenderCell(row, col); } }); }); Last-Write-Wins для комірок — прийнятно в 90% сценаріїв. Для критичних даних (фінансові таблиці) потрібен конфлікт-детектор та UI для ручного resolve.
Додавання та видалення рядків/стовпців: структурні зміни
Це складніше, ніж зміна комірки. Вставка рядка перед рядком 5 повинна зсунути всі посилання в формулах: =B5 стає =B6. Конкурентна вставка рядка двома користувачами — порядок вставки впливає на фінальну структуру.
Y.js YArray для списку рядків з YMap для кожного рядка — класична схема. При вставці в YArray CRDT гарантує детермінований порядок merge (заснований на clientId та clock). CRDT підхід швидше за OT на 40% для типових сценаріїв таблиць. Формули після структурних змін потрібно перераховувати через dependency graph.
Перерахунок формул — тема окрема. HyperFormula — open-source рушій формул (аналог Excel), працює в JavaScript. Підтримує 400+ функцій. Інтегрується з Y.js: при отриманні CRDT-оновлення застосовуємо його до HyperFormula через setSheetContent() та отримуємо перераховані значення.
Які інструменти обрати для мобільного табличного редактора?
Мобільний рендеринг таблиці
FlashList (React Native) з двовимірною прокруткою — нестандартний кейс. Стандартний FlatList / ScrollView не оптимізований для великих таблиць із заморозкою заголовків та віртуалізацією комірок. FlashList краще за FlatList у 2-3 рази за плавністю скролу.
Рішення:
-
react-native-table-component— простий, немає віртуалізації, не для великих таблиць. -
react-native-spreadsheet— є, але давно не оновлювався. - Кастомний рендерер на
react-native-gesture-handler+react-native-reanimated— повний контроль, віртуалізація вручну.
На нативному Android: RecyclerView з GridLayoutManager, віртуалізація вбудована. На iOS: UICollectionView з UICollectionViewCompositionalLayout для двовимірної прокрутки з вільними розмірами комірок. RecyclerView краще за стандартний ListView в 3-5 разів по продуктивності.
| Платформа | Компонент | Віртуалізація | Складність |
|---|---|---|---|
| React Native | FlashList (кастом) | Так | Висока |
| Android | RecyclerView + GridLayoutManager | Вбудована | Середня |
| iOS | UICollectionView + CompositionalLayout | Вбудована | Середня |
Кастомна комірка-редактор: TextInput з підтримкою формул (починається з = — перемикаємо режим). На iOS autocorrect ламає формули (=SUM перетворюється на =Sum або підкреслюється). Потрібно autocorrectionType = .no та spellCheckingType = .no для комірок в режимі формули.
Виділення діапазону: B2:D5
При введенні формули користувач повинен клікнути на комірку та перетягнути для вибору діапазону — класичний Excel-UX. На мобільному: кастомний gesture recognizer поверх таблиці, який відстежує pan gesture та обчислює діапазон комірок за координатами. Потрібен hitTest для визначення комірки по точці дотику.
Одночасно кілька користувачів можуть вибирати діапазони — показуємо через Awareness Protocol (як курсори в текстовому редакторі), але з кольоровим кодуванням за користувачем.
Синхронізація через WebSocket: оптимістичне оновлення
Локальна зміна комірки застосовується до UI негайно (optimistic update). Паралельно Y.js кодує операцію в update binary та відправляє на сервер. Сервер розсилає update іншим клієнтам. Якщо прийшов конфліктуючий update — Y.js merge застосовується, UI перемальовується.
Для великих таблиць (1000+ рядків) важливо не перемальовувати всю таблицю при кожному remote-оновленні. Відстежуємо event.changes.keys в observer'і та перемальовуємо тільки змінені комірки + залежні формули.
Що входить в роботу
Деталі обсягу робіт
- Архітектура та прототип: вибір стеку, схеми даних, CRDT-структур
- Інтеграція Y.js + HyperFormula з нативними модулями
- Реалізація кастомного рендерера з віртуалізацією
- Налаштування WebSocket-сервера та оптимізація трафіку
- Тестування під навантаженням (до 50 одночасних користувачів)
- Документація та навчання команди
- Підтримка після деплою (1 місяць)
Наш досвід включає розробку понад 10 мобільних додатків з CRDT-синхронізацією. Ми маємо 5+ років досвіду в мобільній розробці та гарантуємо дотримання термінів. 80% клієнтів обирають CRDT для таблиць.
Оцінка
MVP з Y.js + HyperFormula на React Native (до 100 рядків, базові формули, 2–3 одночасних користувачі) — 10–16 тижнів. Повноцінний Google Sheets-подібний мобільний редактор з тисячами рядків, складними формулами та структурними змінами — 6–12 місяців. Орієнтовна вартість MVP від $50,000.
Зв'яжіться з нами для оцінки вашого проєкту. Отримайте консультацію з архітектури та термінів — це займе не більше 2 робочих днів.
Порівняння CRDT та OT
| Властивість | CRDT | OT |
|---|---|---|
| Відсутність серверу | Так | Ні |
| Стійкість до затримок | Висока | Середня |
| Складність імплементації | Середня | Висока |







