Rozwiązania do zarządzania stanem budynku dla edytorów Creative Tools

Nov 28 2022
Wprowadzenie W tym artykule podzielimy się niektórymi z naszych (lekko upartych) spostrzeżeń na temat projektowania i budowania skalowalnych rozwiązań do zarządzania stanem w kontekście kreatywnych edytorów narzędzi z React, Immer i Recoil jako fundamentami naszego podejścia. Aby lepiej zilustrować nasze podejście, dołączymy kilka przykładów kodu i mały prototyp.

Wprowadzenie

W tym artykule podzielimy się niektórymi z naszych (lekko upartych) spostrzeżeń na temat projektowania i budowania skalowalnych rozwiązań do zarządzania stanem w kontekście kreatywnych edytorów narzędzi z React, Immer i Recoil jako fundamentami naszego podejścia.

Aby lepiej zilustrować nasze podejście, dołączymy kilka przykładów kodu i mały prototyp.

Korzystaliśmy z tej wiedzy w kilku różnych projektach, co pozwoliło nam łatwo uniknąć niektórych z najczęstszych pułapek.

Stan aplikacji a stan projektu

Jednym z głównych rozróżnień, jakie możemy wprowadzić w tego rodzaju aplikacjach, jest stan aplikacji a stan projektu.

Stan projektowy

Opisuje treści utworzone przez użytkownika, na przykład: tekst (czcionka, rozmiar, kolor, treść), obrazy, elementy zgrupowane, elementy połączone itp.

Stan projektu zmienia się za każdym razem, gdy projekt zmienia się koncepcyjnie, ale nie wtedy, gdy użytkownik wykonuje działania, które nie mają wpływu na projekt.

Niektóre przykłady działań, które mają wpływ na stan projektu:

  • Przesuń element
  • Zmień właściwości tekstu (kolor, rozmiar, czcionka)
  • Elementy grupowe

Definiuje bieżący stan aplikacji, z wyłączeniem projektu. Stan aplikacji zmienia się, gdy użytkownik wchodzi w interakcję z aplikacją i menu.

Kilka przykładów:

  • Wybierz element
  • Otwórz okno dialogowe, aby edytować właściwości elementu
  • Pokaż menu prawego przycisku myszy
  • Potwierdź czynność

Każdy rodzaj stanu ma swoje własne cechy szczególne, które nie mają zastosowania do drugiego i spowodowałyby, że nasza implementacja byłaby bardziej złożona, gdyby były trzymane razem.

Na przykład:

  • Podczas zapisywania projektu zapisywana jest tylko ta część stanu.
  • Cofnij/powtórz dotyczy tylko stanu projektu. Aplikacja musi być w stanie działać w przypadku niewielkich niespójności między projektem a stanem aplikacji, na przykład gdy wybrany element nie znajduje się już w projekcie z powodu cofnięcia.
  • Podczas ładowania projektu stan projektu jest po prostu ustawiany wraz z projektem, podczas gdy stan aplikacji jest głównie inicjowany wartością domyślną.

Stan aplikacji

Kiedy tylko możemy, musimy przedkładać stan lokalny nad stan globalny, stan aplikacji nie jest wyjątkiem.

Analizowanie kodu i jego przepływu staje się łatwiejsze, gdy używamy stanu lokalnego, komponenty muszą jawnie przekazywać dane między sobą, a ich interakcje są jawne, nie ma „daleko ukrytych” efektów użytkowania, które musimy szukać ręcznie.

Stan projektowy

Podczas definiowania stanu projektowego musimy wziąć pod uwagę następujące kwestie:

  • Być w stanie uzyskać i ustawić cały stan, abyśmy mogli łatwo wdrożyć:
    – Ładowanie i zapisywanie projektu
    – Cofnij/ponów
  • Unikaj renderowania wszystkich elementów, gdy zmieniają się tylko niektóre z nich

Jednym z najłatwiejszych sposobów implementacji cofania/ponawiania w aplikacji React jest użycie Immer do generowania łat modyfikujących.

Immer to biblioteka JS, która pozwala nam pisać modyfikacje danych w niezmiennym kontekście (na przykład stan React) w „zmienny sposób”.

Kolejną cechą Immera jest generowanie łatek do i cofania modyfikacji, co pozwala nam zapisać, jak ponownie wykonać dokonaną zmianę i jak wrócić do stanu podglądu.

Po każdej zmianie stanu powinniśmy zapisać łatki do i cofnij i używać ich, gdy użytkownik uruchomi cofanie/ponawianie.

Dlaczego musimy uzyskać i ustawić cały stan w tym samym czasie?

Alternatywą nie jest posiadanie tego razem, nie wydaje się to dużym problemem przy ładowaniu i zapisywaniu, musimy po prostu pobrać/ustawić wszystkie różne części stanu.

Ale staje się problemem, gdy musimy zaimplementować cofanie/ponawianie. Dla każdej części stanu musimy zapisać wyprodukowane łatki, z metadanymi wskazującymi, dla której części stanu jest ona przeznaczona, i odczytać je, gdy uruchomi się cofanie, abyśmy mogli zmodyfikować poprawną część stanu.

Ponadto, ponieważ akcja użytkownika może modyfikować wiele części stanu jako jedną operację, musimy śledzić, które poprawki należą do tej samej operacji, abyśmy mogli je cofać i ponawiać w tym samym czasie.

Użycie jednego stanu rozwiązałoby wszystkie te problemy

  • Nie ma akcji modyfikujących wiele stanów
  • Wszystkie poprawki dotyczą stanu, a nie wielu stanów rozproszonych.

Łatwiejszym sposobem spełnienia tego wyboru projektu jest zapisanie całego stanu projektu w tym samym miejscu. To sprawiłoby, że niektórzy pomyślelibyśmy jak useState<DesignState>(defaultState), jak zapewne się domyślasz, powoduje to, że nie udaje nam się rozważyć „wyrenderowania większości aplikacji”:

Brak renderowania większości aplikacji po zmianie projektu

Aby rozwiązać ten problem, zazwyczaj używamy Recoil, biblioteki zarządzania stanem dla React.

Odrzut ma dwa główne pojęcia : atomy i selektory.

Atomy : jednostka stanu, której można używać w podobny sposób jak useState, ale na przykład globalnie

W ramach useStateimplementacji w powyższym kodzie wszystkie elementy DesignElements będą renderowane za każdym razem, gdy zmieni się jakikolwiek element (lub dowolna część stanu). Aby rozwiązać ten problem, możemy użyć selektorów.

Selektory : funkcje, które tworzą projekcję z atomu lub innego selektora. Gdy komponent React używa selektora, będzie on ponownie renderowany (z pewnymi zastrzeżeniami) tylko wtedy, gdy zmieni się wynik działania selektora.

Recoil pozwala nam również odbierać argumenty w funkcji definiującej selektor, tego rodzaju selektory nazywane są selectorFamily. Możemy użyć tego do stworzenia rodziny selektorów, która otrzyma elementId i przekaże nam ten element.

Gdy element jest modyfikowany, powyższy kod wyzwala tylko aktualizację pasującego DesignElement i nie renderuje wszystkich elementów ani komponentu Design.

Uważny obserwator może zauważyć, że komponent Design będzie renderowany za każdym razem, gdy komponent zostanie dodany lub usunięty, co spowoduje ponowne wyrenderowanie wszystkich elementów DesignElements. Jeśli powoduje to problemy z wydajnością w konkretnym przypadku użycia, możemy opakować nasz komponent DesignElement w plik React.memo.

Ustaw stan

Ponieważ chcemy, aby nasze zestawy były stosowane w DesignState najwyższego poziomu (aby uprościć cofanie/ponawianie), możemy tworzyć wywołania zwrotne odrzutu, aby zawrzeć naszą logikę modyfikacji i tworzenie poprawek cofania/ponawiania.

Minimalny przykład można znaleźć w tym Codesanbox.

Zamknięcie

W tym poście na blogu podzieliliśmy się głównymi czynnikami wpływającymi na projektowanie zarządzania stanem w kontekście edytorów narzędzi kreatywnych oraz naszą implementacją, która je spełnia.

Jeśli przeczytałeś to do końca, chciałbym poznać Twoją opinię. Tutaj możesz się ze mną skontaktować .

W Zeppelin Labs pomagamy założycielom i rozwijającym się firmom eksperymentować i tworzyć zróżnicowane produkty cyfrowe, które napędzają wzrost. Możesz nas znaleźć tutaj . Albo tutaj . Albo tutaj .

Jeśli chcesz dołączyć do naszego zespołu, napisz do nas na adres [email protected]

Chcesz zostać partnerem? napisz do nas na adres [email protected]

Zapisz się do naszego Newslettera tutaj .