Strona główna / Artykuły

D3.js w React: Wiązanie danych, SVG oraz zasada własności z useEffect

Dowiedz się, czym naprawdę jest D3, jak działa wiązanie danych do elementów, jak je wprowadzać, aktualizować i usuwać, dlaczego SVG jest do tego odpowiednie, oraz jak podzielić kontrolę nad DOM, aby D3 i React mogły współpracować przy tworzeniu wykresów słupkowych.

Wydawca: REACTAPP.TOP7 min czytania1530 słów

Źródła: Harnessing the Power of Data Visualization: Integrating D3.js with React

Trzy rzeczy, które należy wiedzieć najpierw

  1. D3 to zestaw narzędzi, a nie biblioteka do tworzenia wykresów. Osoby, które spodziewają się czegoś w rodzaju Chart.js, napotykają trudności. D3 to narzędzie niskiego poziomu służące do łączenia danych z elementami DOM, co tłumaczy zarówno jego moc, jak i trudniejszą ścieżkę nauki.
  2. React i D3 konkurują o te same węzły DOM. Rozwiązanie jest proste: React renderuje kontener SVG, a D3 zarządza wszystkim w jego wnętrzu za pomocą useEffect. Jeśli pominiesz tę zasadę, spędzisz godziny na rozwiązywaniu dziwnych konfliktów.
  3. Wzorzec enter, update i exit na początku wydaje się dziwny. Po kilku próbach staje się on zrozumiały, a inne podejścia do grafiki opartej na danych wydają się nieudolne.

Czym właściwie jest D3

Nazwa oznacza Dokumenty Zasilane Danymi. Kluczowym słowem jest „dokumenty”: elementy DOM, których wygląd jest określany przez dane. D3 łączy tablice z elementami i umożliwia określenie ich wyglądu na podstawie wartości zawartych w tych tablicach. Wykres to tylko jeden z możliwych wyników, a sama biblioteka nie ma pojęcia o typach wykresów.

Stąd pochodzą zarówno zalety, jak i trudności. D3 nie podejmuje żadnych decyzji za Ciebie. Każda właściwość wizualna, każda oznaka osi, każdy kolor oraz każda interakcja musisz określić sam. Otrzymujesz pełną swobodę twórczą, ale też dużo pracy pisarskiej.

Łączenie danych z DOM

To właśnie tutaj większość początkujących rezygnuje, więc warto to dokładnie przejść krok po kroku.

W zwykłym JavaScript tworzysz element, wypełniasz go treścią i wstawiasz na stronę. Z D3 wybierasz grupę elementów, w tym te, które jeszcze nie istnieją, przypinasz do tej grupy tablicę, a następnie otrzymujesz z powrotem trzy grupy:

  • elementy, które już istnieją i potrzebują jedynie nowych danych (grupa do aktualizacji),
  • punkty danych, które jeszcze nie mają elementu (grupa do dodania),
  • elementy, których dane zniknęły (grupa do usunięcia).

Dziwne jest wywoływanie metod na pustym wyborze. Wybierasz wszystkie elementy rect wewnątrz SVG, które nie zawierają żadnych takich elementów, wywołujesz .data(myArray), potem .enter(), a następnie .append('rect') – i pojawiają się prostokąty. W rzeczywistości .enter() tworzy zastępcę dla każdego danych, który nie ma odpowiadającego mu elementu, a .append() zamienia każdy z tych zastępców w rzeczywisty węzeł. Można to interpretować jako „dla każdego punktu danych bez elementu utwórz jeden”.

Zanim to zadziała, kod D3 wygląda jak magia. Po tym funkcjonuje naturalnie.

Ustawianie właściwości wizualnych na podstawie danych

Gdy dane zostaną powiązane, kolejny krok jest łatwiejszy do zrozumienia. Każdy atrybut lub styl może zostać obliczony na podstawie tych danych:

selection.attr('height', d => yScale(d.value))

Tutaj d to dane przypisane do danego elementu, a yScale to funkcja konwertująca zakres wartości na piksele. Wysokość słupka jest bezpośrednią funkcją jego danych. Zmień dane, uruchom kod ponownie, a wysokość się dostosuje. Prawie wszystko w wizualizacjach D3 sprowadza się do wariantów tej idei: ustawienie właściwości wizualnej jako funkcji wartości granicznej.

Dlaczego SVG?

D3 rysuje na SVG, a powody tego wykraczają poza kwestie historyczne.

SVG opiera się na wektorach, więc kształty są definiowane matematycznie, a nie jako siatki pikseli. Wykres słupkowy, który wygląda wyraźnie na laptopie, będzie równie wyraźny na ekranie 4K bez żadnego dodatkowego wysiłku, a pliki eksportowane nie będą rozmyte przy nieodpowiednich rozdzielczościach.

Dla interaktywności ważne jest to, że kształty SVG to zwykłe elementy DOM i biorą udział w systemie zdarzeń przeglądarki tak samo jak przycisk. Efekt hover, kliknięcie i dotyk działają bez konieczności specjalnych testów zderzeń, ponieważ wystarczy dodać odpowiednie słuchacze.

Koszty stają się widoczne w dużych skali. Tysiące węzłów SVG spowalniają stronę, więc rysowanie 50 000 oddzielnych punktów jako osobnych elementów będzie miało negatywny wpływ. Renderowanie na canvas lepiej radzi sobie w takich sytuacjach. Dla paneli kontrolnych, wykresów biznesowych i większości narzędzi analitycznych SVG jest wystarczający, a integracja z systemem zdarzeń jest tego warta.

Interaktywność i elastyczność

Interfejsy do eksploracji danych, w których użytkownicy dzielą, filtrowają i analizują dane z różnych perspektyw, to obszar, w którym D3 przewyższa statyczne narzędzia do tworzenia wykresów. Można w nich kontrolować każde zdarzenie i przejście.

Większość bibliotek do tworzenia wykresów oferuje gotowe typy wykresów do konfiguracji: słupkowe, liniowe, koliste, z opcjami dotyczącymi kolorów, etykiet i legend. D3 dostarcza natomiast elementy budulcowe: skale, osie, generatorzy ścieżek oraz algorytmy układu. Typ wykresu to po prostu ich kombinacja, więc nie istnieje lista tego, co jest obsługiwane. Jeśli jakiejś wizualizacji nigdzie nie ma, możesz ją stworzyć, a ograniczeniem będą tylko zasady SVG i matematyka.

Cena to dodatkowa praca. To, czy jest to opłacalne, zależy od projektu. Do standardowych wykresów biznesowych wystarczą Recharts lub Chart.js, i można przejść dalej. W przypadku niestandardowych, interaktywnych grafik, gdzie kluczowa jest dokładna prezentacja, D3 jest lepszym wyborem.

Funkcje, których będziesz używać regularnie

To nie jest pełna referencja, tylko te funkcje, które pojawiają się w codziennej pracy:

  • d3.select() pobiera pierwszy element pasujący do selektora. W React d3.select(svgRef.current) to standardowy sposób na uzyskanie dostępu do kontenera.
  • d3.selectAll() zwraca wszystkie pasujące elementy. Wiązanie danych zazwyczaj zaczyna się tutaj, ponieważ angażuje się kilka elementów.
  • selection.data() łączy każdy element z tablicą, dzięki czemu d jest dostępne w późniejszych wywołaniach. Wszystko inne zależy od tego kroku.
  • selection.enter() tworzy tymczasową selekcję dla danych, które nie mają węzła, a .append() tworzy następnie te węzły.
  • selection.attr() ustawia atrybuty, na przykład linię wysokości pokazaną powyżej, co będziecie pisali setki razy.
  • selection.style() robi to samo dla CSS, np. ustala kolor wypełnienia lub stopień przenikania, który zależy od tego, czy element jest aktywny.
  • selection.transition() animuje zmiany. Umieść go przed .attr() lub .style(), a wartości będą się gładko zmieniać, zamieniając nagłe zmiany w animację.
  • d3.scaleLinear() przekształca wartości z jednego przedziału liczbowego do drugiego. Przy domenie od 0 do 100 i zakresie od 0 do 400 wartość 50 odpowiada pikselowi 200. Skale występują praktycznie we wszystkich wizualizacjach.
  • d3.axisBottom() i d3.axisLeft() tworzą osie z podziałkami i etykietami pochodzącymi ze skali, oszczędzając czas potrzebny na ręczne rysowanie oznaczeń.
  • d3.max() zwraca największą wartość, często używaną do ustawienia domen skali, np. w d3.max(data, d => d.value).
  • d3.transition() koordynuje animacje kilku wybranych elementów jednocześnie.
  • Używanie D3 w React

    Problem, o którym nikt nie wspomina, dopóki się z nim nie zderzysz: React utrzymuje wirtualny DOM i wprowadza własne zmiany w tym rzeczywistym, podczas gdy D3 edytuje rzeczywisty DOM bezpośrednio. Jeśli obie biblioteki działają w obrębie jednego elementu, ponowne renderowanie w React nadpisuje prace D3, albo D3 dodaje węzły, o których React nie wie, co powoduje zamieszanie przy następnym renderowaniu.

    Skutecznym rozwiązaniem jest podział obowiązków. React renderuje kontener SVG i nie ingeruje w jego zawartość. D3 dostępuje do tego kontenera za pomocą ref i wykonywa wszystkie swoje operacje w funkcji useEffect. Gdy dane się zmieniają, aktualizacja stanu uruchamia tę funkcję, a D3 usuwa stare elementy i rysuje je na nowo. Żadna z bibliotek nie ingeruje w pracę drugiej.

    1. Zainstaluj pakiety

    Dodaj D3 obok React i ReactDOM za pomocą swojego menedżera pakietów.

    npm install d3 react react-dom
    

    Użytkownicy Yarn robią to samo za pomocą własnego polecenia add.

    yarn add d3 react react-dom
    

    2. Napisz komponent

    Strona React ma jeden zadanie: wyświetlić element SVG i przekazać D3 odniesienie do niego. Nic więcej w wizualizacji nie należy do React.

    3. Rysuj w useEffect

    Wewnątrz efektu napisz kod D3 tak samo, jak robiłbyś to poza React, używając odniesienia do znalezienia kontenera. Dwie skale zajmują się obliczeniami współrzędnych – jedna mapuje etykiety kategorii na pozycje poziome, a druga mapuje wartości danych na pozycje pionowe. Wysokość każdej kolumnki to dolna krawędź wykresu minus przeskalowana wartość. Ta część sprawia trudności, ponieważ oś y w SVG skierowana jest w dół, więc wyższe kolumnki odpowiadają mniejszym wartościom y.

    4. Wyświetl komponent

    Umieść komponent w swojej aplikacji i przekaż mu tablicę danych. Zmień tablicę, a wykres zostanie ponownie narysowany: React zauważa zmienioną właściwość, uruchamia efekt, a D3 usuwa stare dane i rysuje nowe. Połączenie między stanem aplikacji a wizualizacją jest automatyczne.

    5. Uruchom aplikację

    Zainstaluj i uruchom serwer rozwojowy za pomocą npm.

     npm start
    

    Lub z Yarn.

     yarn start
    

    W przeglądarce zobaczysz wykres słupkowy. Od tego momentu wszystko opiera się na tym samym wzorcu: podpowiedzi przy przewijaniu, skale kolorów, animowane aktualizacje, powiększanie oraz inne typy wykresów. Wszystko to znajduje się w tym efekcie i wykorzystuje funkcje wymienione powyżej.

    Podsumowanie

    D3 i React współpracują, gdy struktura jest prawidłowa. React odpowiada za kontener i cykl życia komponentu, natomiast D3 za rysowanie w jego wnętrzu. Zachowaj jasną granicę między nimi, aby każda biblioteka pełniła swoją funkcję.

    Zaletą jest możliwość tworzenia wizualizacji, których nie oferuje żaden pakiet do wykresów, ponieważ pracujesz z elementami podstawowymi, a nie z opcjami konfiguracji. Gdy sam graf jest częścią wartości produktu, ma to znaczenie. Jeśli panel sterowania potrzebuje jedynie wykresu liniowego, użyj Recharts i uniknij dodatkowych kroków konfiguracji.

    Literatura pokrewna