React в плагине Sketch: проблема загрузки исходных данных

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

Мы также рассмотрим одно из возможных решений этой проблемы. В конце статьи есть ссылка на полный демо-репозиторий.

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

Три компонента плагина с UI React

Плагин Sketch, использующий UI React, можно разделить на три части.

1. Фронтенд

Это веб-браузер (включая DevTools). Это WebView, API которого имитирует API BrowserWindow фреймворка Electron.

WebView “плавает” в отдельном окне поверх UI Sketch. Он обменивается информацией с бэкендом с помощью комбинации window.postMessage (отправка данных) и слушателей событий (получение данных).

2. UI React

Именно в WebView загружается и отображается UI React так же, как при запуске обычного приложения React в веб-браузере.

3. Бэкенд

Это часть плагина имеет связь с данными в документации Sketch. Бэкенд использует API разработчика Sketch.

Бэкенд обменивается информацией с фронтендом с помощью слушателей событий посредством объекта webContents.

Проблема загрузки исходных данных

Загружаемые исходные данные  —  это данные, которые отправляются с бэкенда в UI React после завершения загрузки WebView (фронтенда).

Слишком ранняя отправка данных

Во время разработки плагина я заметил, что при его запуске UI React иногда не получал никаких данных Sketch.

Я попытался выяснить причину. В конце концов разобрался и сформулировал пошаговое объяснение того, что происходит при запуске плагина.

Что происходит при запуске плагина

  1. Бэкенд открывает WebView (фронтенд) и ожидает события did-finish-load, которое должно исходить от WebView.
  2. Фронтенд обнаруживает код JavaScript, который загружает React UI.
  3. React UI прослушивает событие send-data.
  4. Фронтенд выдает событие did-finish-load, сообщая бэкенду, что загрузка завершена.
  5. Бэкенд распознает событие (did-finish-load) и отправляет данные с помощью события send-data.
  6. UI React распознает событие (send-data) и заполняет UI данными события.

Проблема

Я полагал, что все происходит синхронно, но оказалось, что это не всегда так. Иногда шаг 4 происходит раньше шага 3, что приводит к сценарию, при котором бэкенд отправляет данные до того, как UI React будет готов.

Вследствие этого в UI React не попадают данные Sketch.

Решение

Мы изменили порядок обмена данными. Вместо того, чтобы бэкенд прослушивал и отвечал на событие did-finish-load, мы указали React UI запрашивать данные, как только они будут готовы.

Это потребовало внесения некоторых изменений в код.

my-command.js

В отличие от события did-finish-load, событие get-data не вызывается автоматически WebView. Событие get-data срабатывает только после вызова.

// Меняем
webContents.on('did-finish-load', () => {

// на
webContents.on('get-data', () => {

useSketchData.js

Мы добавили функцию refreshSketchData в хук React, который запускает слушатель событий бэкенда get-data. Это заставляет бэкенд плагина отвечать данными Sketch с помощью события send-data.

import React, {useState, useEffect } from 'react';

const useSketchData = (initialValue) => {
const [sketchData, setData] = useState(initialValue);

useEffect(() => {
const handleSendDataEvent = (e) => {
setData(e.detail.data);
}

window.addEventListener("send-data", handleSendDataEvent);
return () => {
window.removeEventListener("send-data", handleSendDataEvent);
}
}, [])

const setSketchData = (data) => {
window.postMessage('setSelectionName', data);
setData(data);
}

// Добавим это, чтобы запросить данные из "бэкенда"
const refreshSketchData = () => {
window.postMessage('get-data');
}

return [sketchData, setSketchData, refreshSketchData];
}

export default useSketchData;

App.jsx

Мы добавили useEffect, который срабатывает один раз, когда UI React готов к обработке данных Sketch. В useEffect вызываем недавно созданную функцию  —  refreshSketchData.

import React, { useEffect } from 'react';
import useSketchData from '../hooks/useSketchData';

const App = () => {
const [sketchData, setSketchData, refreshSketchData] = useSketchData();

// Срабатывает один раз, когда React UI готов к обработке данных
useEffect(() => {
console.log("refresh data!");
refreshSketchData();
}, []);

const handleButtonClick = (e) => {
const newName = `${sketchData} 🦖`;
setSketchData(newName);
}

return(<div>
<p>{sketchData}</p>
<button onClick={handleButtonClick}>Watch out for the dinosaur!</button>
</div>)
}

export default App;

Вот и все! Изменив порядок загрузки исходных данных, мы предотвратили рендеринг UI React без данных Sketch.

Полная версия демо-репозитория находится здесь.

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Fredrik Ward: How to use React in your Sketch plugin — The Problem with Initial Data Load

Предыдущая статья3 приема для определения функций в Python
Следующая статьяVS Code Remote-SSH для удаленной разработки