WordPress

javascript – W jaki sposób mogę uzyskać dostęp do danych o stanie edytora witryn i bloków oraz użyć `useSelect()` lub `withSelect()`, aby powiązać je z moimi komponentami?

  • 25 czerwca, 2021
  • 10 min read
javascript – W jaki sposób mogę uzyskać dostęp do danych o stanie edytora witryn i bloków oraz użyć `useSelect()` lub `withSelect()`, aby powiązać je z moimi komponentami?


Podstawowy model danych Gutenberga dzieli wiele z API i ideologii biblioteki zarządzania stanem Redux, z wieloma różnicami. Zapoznanie się z Redux może dać wiele wglądu w to, jak działa zarządzanie danymi Gutenberga.

Wykład ten będzie częścią egzaminu końcowego.


Architektura stanu aplikacji

Stan aplikacji Block Editor jest podzielony pomiędzy szereg różnych „magazynów danych” odpowiedzialnych za zarządzanie określonym zakresem. Zobacz Data Module Reference w Block Editor Handbook, aby uzyskać przegląd każdego magazynu.

Każdy zawiera prywatny, niedostępny z zewnątrz obiekt reprezentujący zakres stanu aplikacji. Oprócz obiektu stanu, każdy magazyn składa się również z trzech podstawowych komponentów (między innymi):

  • Twórcy akcji są funkcjami, które przyjmują argumenty i wykonują niezbędną pracę, aby przekształcić te argumenty w zdarzenie z ładunkiem ( Działanie) i wyzwalają to zdarzenie w magazynie danych („wysyłają Akcję”). Twórcy Akcji mogą wykorzystywać zewnętrzne efekty uboczne w procesie tworzenia swoich odpowiednich Akcji — obejmuje to takie rzeczy, jak wykonywanie zapytań do REST API w celu uzyskania danych. Twórcy Akcji są udostępniani zewnętrznie, więc możemy ich wywoływać, aby wprowadzić pożądane zmiany w stanie aplikacji.
  • Reduktory nasłuchują akcji wysyłanych do ich magazynu i przetwarzają je, aby zaktualizować dane stanu magazynu (jeśli to konieczne). Są one niedostępne z zewnątrz — więc gdy magazyn zostanie skonstruowany, nie możemy już dodawać ani modyfikować sposobu, w jaki magazyn danych reaguje na wysyłane akcje. Reduktory są również „czystymi funkcjami” lub funkcjami, które nie zależą od zewnętrznych efektów ubocznych. Przy tych samych argumentach czysta funkcja zawsze zwróci tę samą wartość zwracaną; lub w naszym przypadku, przy tej samej akcji, Reduktor Gutenberga zawsze zmodyfikuje stan magazynu za pomocą tej samej, przewidywalnej logiki.
  • Selektory są funkcjami, które pobierają dane ze stanu magazynu, potencjalnie kształtując je w coś bardziej użytecznego niż surowe wartości w procesie. W Gutenbergu selektory mogą wywoływać Action Creators w celu wypełnienia brakującego stanu – to ważny punkt, do którego przejdziemy później.

Podsumowując, Twórcy akcji skutecznie umożliwiają nam zapisywanie zmian w stanie aplikacji i Selektory umożliwi nam jego przeczytanie. Akcje wysłane przez Twórca akcji funkcje są konsumowane przez Reduktory które faktycznie modyfikują obiekt stanu – ale oba Akcje I Reduktory są zazwyczaj niedostępne z zewnątrz, w głównych magazynach danych i mają znaczenie tylko wtedy, gdy tworzysz własny magazyn danych w celu zarządzania stanem aplikacji motywu lub wtyczki.

Narzędzia programistyczne

Rozszerzenie Redux DevTools Chrome jest nieocenionym narzędziem do zrozumienia stanu aplikacji w aplikacji opartej na Redux (lub aplikacji zgodnej z Redux). Umożliwia ono sprawdzenie obiektu stanu przechowywanego w magazynach Gutenberga, a także sekwencji wysyłanych akcji i sposobu, w jaki modyfikują one stan w czasie, wśród wielu innych przydatnych funkcji.


Interfejs magazynu danych

Twórcy akcji

W Gutenbergu możemy uzyskać obiekt zawierający wszystkich publicznych twórców akcji sklepu, przekazując nazwę (lub obiekt definicji) sklepu @wordpress/data’S dispatch() funkcja. To jest zasadniczo nasz sposób na „zapisanie” stanu tego sklepu.

Warto przeczytać!  Jak dodać autouzupełnianie pól adresowych w WordPress

Selektory

Podobnie jak w przypadku kreatorów akcji, możemy pozyskać obiekt zawierający wszystkie funkcje, których możemy użyć do pobrania danych stanu ze sklepu, przekazując jego nazwę/definicję select():

import { select } from '@wordpress/data';

console.log( select( 'core/block-editor' ).isTyping() );

Niektóre selektory Gutenberga po wywołaniu faktycznie wysyłają akcję w celu pobrania danych, których brakuje w magazynie, co powoduje nietypową sytuację, w której po pierwszym wywołaniu selektora zwraca on undefined (lub jakakolwiek początkowa wartość właściwości w stanie), ale kolejne wywołanie chwilę później zwróci oczekiwane dane:

import { select } from '@wordpress/data';

const logPublicPostTypes = () => console.log( select( 'core' ).getPostTypes( { public: true } ) );

logPublicPostTypes() // Logs `null`.

setTimeout( logPublicPostTypes, 1000 ); // Logs an array of post type objects.

Wnioskiem z tego jest to, że Selektory zapewniają synchroniczny dostęp do danych w państwie – nawet jeśli wyślą Akcję, która wkrótce zmieni dokładnie ten stan, który właśnie przywrócili.


Prenumerata

Jak wykazano powyżej, nie zawsze możemy przewidzieć, czy Selektor zwróci dane, których potrzebujemy natychmiast (takie jak isTyping() Selektor powyżej) lub czy może wysłać Akcję w celu pobrania tych danych i zaktualizowania stanu magazynu po fakcie (jak wszystko, co zależy od danych pobranych z REST API, takie jak dostępne typy postów). Na szczęście magazyny danych zapewniają subscribe() funkcja w celu dołączenia wywołań zwrotnych, które będą wykonywane za każdym razem, gdy akcja zostanie wysłana do sklepu. Co więcej, w Gutenbergu są one wykonywane tylko wtedy, gdy stan faktycznie się zmienia.

@wordpress/data ujawnia globalny subscribe() funkcja, która będzie wykonywać swoje wywołania zwrotne zawsze, gdy każdy zmiany stanu sklepu. Możemy więc użyć go do obserwowania asynchronicznie ładowanych danych lub określonych zmian stanu.

subscribe() zwraca również funkcję, którą można wywołać w celu usunięcia subskrypcji, co pozwala na odłączenie funkcjonalności, dla której aktualizacje stanu nie są już istotne:

import { subscribe, select } from '@wordpress/data';

const { getPostTypes } = select( 'core' );

const unsub = subscribe(
  () => {
    const public_post_types = getPostTypes( { public: true } );

    if( ! public_post_types )
      return;
    
    doCoolThingsWithPostTypes( public_post_types );
    unsub();
  }
);

function doCoolThingsWithPostTypes( post_types ) {
  // ...
}

Integracja komponentów React

Jak szczegółowo opisano powyżej, selektory używane do uzyskiwania dostępu do danych stanu w magazynach zapewniają jedynie natychmiastowy i synchroniczny migawkę przechowywanej wartości. Tak więc użycie selektora do pobierania danych w komponencie daje jedynie bieżącą wartość w stanie, gdy komponent jest renderowany. Co jeśli chcesz, aby komponent React był ponownie renderowany z najnowszą wartością, gdy wartość zmienia się w stanie?

Warto przeczytać!  Numery wersji typu wpisu

Oba useSelect() hak i withSelect() Narzędzia „komponentów wyższego rzędu” służą temu samemu celowi — zapewniają mechanizm udostępniania danych o stanie ze sklepów do komponentu i jednocześnie tworzą subskrypcję w celu ponownego renderowania tego komponentu po zmianie danych o stanie.

Wybór jednego z nich zależy głównie od osobistych preferencji, z zastrzeżeniem, że useSelect() można używać wyłącznie wewnątrz komponentu funkcjonalnego. W większości przypadków jednak haki takie jak useSelect() są powszechnie uważane za bardziej elegancki interfejs, rozwiązujący wiele niedogodności inherentnych dla bardziej tradycyjnego wzorca „komponentów wyższego rzędu” (zobacz również to opracowanie na temat haków autorstwa współtwórcy Redux).

Moim zdaniem łatwiej jest zrozumieć wewnętrzny mechanizm HOC-ów, ale haki są łatwiejsze do nauczenia się w użyciu, generują bardziej przejrzysty i łatwiejszy w utrzymaniu kod oraz ograniczają wiele zanieczyszczeń związanych z „piekłomieniem opakowań” w hierarchii komponentów.

Ten withSelect() Pożytek

Wbrew intuicji „komponenty wyższego rzędu” nie są w rzeczywistości komponentami, lecz funkcjami, które otrzymują komponent jako argument i otaczają go dodatkowymi komponentami i funkcjonalnościami.

Czasami nazwa ta jest również stosowana do pewnego rodzaju „curried HOC”, gdzie funkcja nie przyjmuje bezpośrednio komponentu jako argumentu i nie zwraca jego opakowanej wersji, ale zwraca nową funkcję, która spełnia tę definicję. Opisuje to @wordpress/data’S withSelect() i tak samo jak Redux connect(); obie przyjmują szereg argumentów, które nie są komponentami, a następnie zwracają funkcję spełniającą definicję HOC — zwracane funkcje przyjmują komponent jako argument i opakowują go dodatkową funkcjonalnością przed zwróceniem opakowanego komponentu.

Podstawowe użytkowanie

withSelect() otrzymuje pojedynczy argument – ​​funkcję wywołania zwrotnego, która odbiera @wordpress/data’S select() funkcja i dostarczone przez komponent właściwości jako argumenty, i oczekuje się, że zwróci obiekt zawierający właściwości komponentu pochodzące z selektorów lub undefined. Wewnętrznie, withSelect() subskrybuje odpowiednie sklepy, dzięki czemu funkcja wywołania zwrotnego jest wykonywana w celu ponownego obliczenia mapowania przy każdej zmianie stanu.

HOC, który withSelect() następnie można użyć zwrotów, aby dodać te właściwości pochodzące z selektora do komponentu. Gdy wartości w mapowaniu ulegną zmianie z powodu wewnętrznej subskrypcji sklepu, właściwości komponentu ulegną zmianie i wywołają ponowne renderowanie z nowymi danymi.

Więc możemy użyć withSelect() aby utworzyć niestandardowy HOC, który będzie wypełniał postTypes prop dla komponentu:

const withPublicPostTypes = withSelect(
  ( select ) => ( {
      postTypes: select( 'core' ).getPostTypes( { public: true } ),
  } )
);

const PostTypeList = ( { title="Post Types", postTypes = [] } ) => (
  

{title}

    {postTypes.map( type => (
  • {type.name}
  • ) )}
); const PublicPostTypeList = withPublicPostTypes( PostTypeList );
Korzystanie z rekwizytów

Oprócz ponownego obliczania mapy właściwości przy zmianach stanu, będzie ona również ponownie obliczana, gdy rekwizyty otrzymane przez komponent opakowania HOC ulegną zmianie. Możemy więc nawet udostępnić nowy postTypeArgs podporę i przetłumaczyć na postTypes rekwizyt dla zawiniętego komponentu (używając tego samego PostTypeList składnik z góry):

const withPostTypes = withSelect(
  ( select, ownProps ) => {
    const { getPostTypes } = select( 'core' );
    const { postTypeArgs = {} } = ownProps;

    return {
      postTypes: getPostTypes( postTypeArgs ),
    };
  }
);

const QueryablePostTypeList = withPostTypes( PostTypeList );

Ten useSelect() Hak

React Hooks (nie mylić z hakami PHP action/filter WordPress) to stosunkowo nowy dodatek do React API i służą jako wzorzec do bardziej eleganckiego eksponowania różnych funkcjonalności do wykorzystania wewnątrz komponentów funkcyjnych (komponentów napisanych jako funkcje, a nie klasy). Przed hookami nie było łatwej metody używania stanu lub dołączania funkcjonalności związanych z cyklem życia komponentu przy użyciu bardziej zwięzłej składni komponentu funkcjonalnego.

Warto przeczytać!  Jak uniknąć problemów z serwerem powodujących błędy 404 w WordPress

Podstawowe użytkowanie

useSelect() przyjmuje funkcję wywołania zwrotnego, której podano select() funkcja i oczekuje się, że zwróci wartość pochodzącą z selektorów, podobnie jak funkcja zwrotna przekazywana do withSelect().Możemy wdrożyć składnik z ostatniej sekcji, używając useSelect() podłącz w następujący sposób:

const PublicPostTypeList = ( props ) => {
  const postTypes = useSelect( ( select ) => select( 'core' ).getPostTypes( { public: true } ) );

  return ;
}

Teraz, postTypes zawsze będzie mieć najnowszą wartość publicznej listy typów postów ze sklepu. Jeśli ta lista ulegnie zmianie (np. jeśli ten stan nie został wypełniony przed pierwszym renderowaniem tego komponentu), useSelect()Wewnętrzna subskrypcja spowoduje automatyczne ponowne renderowanie.

Argument zależności i memoizacja

Drugi argument do useSelect() jest tablicą wartości zależności. Jeśli określono, to po pierwszym renderowaniu, useSelect() wykona nasze wywołanie zwrotne mapowania tylko wtedy, gdy jedna lub więcej wartości w tej tablicy ulegnie zmianie, a w przeciwnym razie dostarczy zapamiętaną (buforowaną) wartość z poprzedniego uruchomienia. Jest to przydatna optymalizacja w przypadkach, gdy wartości pochodzące z selektorów uwzględniają właściwości lub inne wartości zewnętrzne, a zmiany stanu nie są dla Ciebie istotne, chyba że te wartości również uległy zmianie:

const QueryablePostTypeList = ( { postTypeArgs = {}, ...props } ) => {
  const postTypes = useSelect(
    ( select ) => select( 'core' ).getPostTypes( postTypeArgs ),
    [ postTypeArgs ]
  );

  return ;
}

Haki niestandardowe

Jak wszystkie inne haki, useSelect() można również użyć podczas tworzenia niestandardowego haka w celu stworzenia wielokrotnego użytku funkcjonalności z wielu haczyków lub w celu zapewnienia uproszczonego interfejsu:

const usePostTypes = ( args = {} ) => useSelect(
  ( select ) => select( 'core' ).getPostTypes( args ),
  [ args ]
);

const PublicPostTypeList = ( props ) => {
  const postTypes = usePostTypes( { public: true } );

  return ;
}


Źródło