Szczegółowy samouczek: jak korzystać z Shopfront API Storefront z React i Redux

E-commerce dla wszystkich! (… Strony internetowe, czyli )

Wpisany przez Chris August 2018, zaktualizowany listopad, 2018

Dzięki uprzejmości Negative Space na pexels.com

Tło i motywacja

Motywacja tutaj była więc dość prosta. Chciałem, aby użytkownicy mojej witryny mogli przeglądać, wyszukiwać i wybierać produkty bezpośrednio w mojej domenie niestandardowej bez konieczności wchodzenia na naszą stronę Shopify.

Druga motywacja polega na tym, że wolę mieć własną bazę kodów dla strony internetowej niż korzystać z jednego z fabrycznych szablonów Shopify. Bez obrazy Zespół Shopify! Szablony są nowoczesne i czyste, ale są raczej podstawowe. Jestem pewien, że te szablony można w dużym stopniu dostosować, ale w tej chwili nie znam stosu.

To jest najlepsze z obu światów - moja niestandardowa strona React (już zbudowana i online ), z dodanym API i procesem kasowym Shopify!

Pod koniec tego samouczka będziesz mógł dodawać swoje produkty Shopify na dowolnej stronie swojej witryny. Jedyną częścią procesu zakupów, która będzie miała miejsce w Shopify, jest kliknięcie przez użytkownika opcji „Do kasy”.

Dla tego samouczka utworzyłem również puste repozytorium.

Motywacją do pisania tutaj na Medium było po prostu to, że sam nie mogłem znaleźć samouczka na temat tego procesu - więc postanowiłem go stworzyć!

Jestem profesjonalnym programistą od 4 lat i programuję dla 7. Pracowałem w technologicznych stosach od starej szkoły Fortran i Perl, po React, Javascript, Python i Node.

Siren Apparel jest jedną z moich pobocznych firm projektowych / startupów / producentów, którą prowadzę od 5 lat i do tej pory przekazaliśmy darowizny na rzecz 5 różnych policji i straży pożarnej!

Na koniec zacznijmy od tego samouczka.

Shopify API Storefront

Wspaniali ludzie z Shopify stworzyli API Storefront. Za pomocą interfejsu API Storefront możesz tworzyć komponenty React w celu dodawania zdjęć produktów, odmian produktów, rozmiarów produktów, koszyka oraz przycisków „dodaj do koszyka” i „kasy” we własnej witrynie innej niż Shopify.

* Uwaga: ten samouczek NIE dotyczy Shopify Polaris, który służy do tworzenia komponentów w samym React for Shopify do zarządzania sklepem.

Rozpoczęcie pracy: Reaktywuj js-buy Repozytorium

Spójrz na ten przykład React opracowany przez zespół Shopify. Większość kodu w tym samouczku pochodzi z tego repozytorium.

… Spojrzałeś? Dobry!

Teraz wskoczymy do kodu! Przejdź do folderu głównego swojej witryny React i zainstaluj moduł shopify-buy za pośrednictwem terminala:

cd my-awesome-reag-project /
npm install - zapisz shopify-buy

(lub przędzy dodaj shopify-kup, jeśli wolisz przędzę)

Następnie we frontend index.js (NIE App.js!) Musisz zaimportować klienta z JS Buy SDK:

importować klienta z „shopify-buy”;

Następnie dodaj następujący obiekt konfiguracji powyżej wywołania ReactDOM.render ():

const client = Client.buildClient ({
    storefrontAccessToken: „twój-dostęp-token”,
    domena: „twoja-shopify-url.myshopify.com”
});

To wszystko na razie dla index.js - wrócimy do tego wkrótce.

Teraz dodamy wszystkie elementy potrzebne do płynnego robienia zakupów i realizacji transakcji. Skopiuj wszystkie komponenty z repozytorium React-Js-Buy:

Cart.js

LineItem.js

Product.js

Products.js

VariantSelector.js

Wkleimy te składniki do folderu acomponents / shopify / w folderze src /. Możesz umieścić te pliki składowe gdziekolwiek w folderze src /, jeśli chcesz. Reszta samouczka zakłada, że ​​umieściłeś je w komponentach / shopify /.

Modyfikowanie pliku App.js

App.js będzie wymagał obszernych zmian. Najpierw zaimportuj właśnie skopiowany składnik koszyka do własnego projektu:

importuj koszyk z „./components/shopify/Cart”;

Jeśli komponent App.js był bezstanowy, podobnie jak mój, powinieneś bezpiecznie skopiować całą funkcję konstruktora ():

konstruktor () {
    Wspaniały();
    this.updateQuantityInCart = this.updateQuantityInCart.bind (this);
    this.removeLineItemInCart = this.removeLineItemInCart.bind (this);
    this.handleCartClose = this.handleCartClose.bind (this);
}

Jeśli masz już stan, skopiuj tylko te linie wiązania. Te trzy wiersze są funkcjami obsługi zdarzeń, których koszyk Shopify potrzebuje do prawidłowego funkcjonowania.

„A co ze stanem dla koszyka !?”

Możesz zapytać; lub:

„A co ze zdefiniowaniem tych procedur obsługi zdarzeń dla koszyka !?”

Rzeczywiście, nadchodzi, ale jeszcze nie!

Następnie możesz dołączyć komponent na dole funkcji render (), przed końcowym div.

Moim zdaniem koszyk powinien być dostępny w dowolnym miejscu w Twojej aplikacji. Myślę więc, że sensowne jest umieszczenie komponentu w komponencie głównym aplikacji - innymi słowy App.js:

powrót (
...
);

Ponownie nie dodałem jeszcze kodu do procedur obsługi zdarzeń koszyka. Ponadto nie rozwiązałem problemu braku składników stanu dla koszyka w App.js.

Jest ku temu dobry powód.

Mniej więcej w połowie tego projektu zdałem sobie sprawę, że mojego produktu nie ma oczywiście w pliku App.js.

Zamiast tego pochowano około trzech elementów podrzędnych.

Zamiast więc przekazywać produkty o trzy poziomy w dół dzieciom, a następnie obsługi funkcji do samego końca…

Zdecydowałem się użyć…

Redux !!!

Ugh! Wiem, wiem, Redux, choć nie jest bardzo trudne, jest uciążliwe w% * $! najpierw podłączyć wszystkie wymagane płyty kotłowe. Ale jeśli jesteś programistą pracującym w sklepie e-commerce lub właścicielem sklepu e-commerce, pomyśl o tym w ten sposób: Redux umożliwi ci dostęp do stanu koszyka z dowolnego komponentu lub strony w naszej witrynie internetowej lub aplikacji internetowej.

Ta umiejętność będzie niezbędna w miarę rozszerzania się syreny i opracowywania kolejnych produktów. W miarę tworzenia kolejnych produktów utworzę osobną stronę sklepu ze wszystkimi produktami, pozostawiając tylko garść polecanych produktów na stronie głównej.

Dostęp do koszyka jest niezbędny, jeśli użytkownik trochę robi zakupy, czyta historie lub informacje o odzieży Siren, a następnie decyduje się na zakup. Nie ma znaczenia, jak dużo się poruszają, nic z koszyka nie zostanie utracone!

Krótko mówiąc, zdecydowałem, że prawdopodobnie lepiej jest teraz wdrożyć Redux, podczas gdy podstawa kodu dla naszej witryny nie jest zbyt duża.

Wdrażanie Redux dla Shopify Kup SDK z gołym minimalnym szablonem

Zainstaluj pakiety NPM redux i React-Redux:

instalacja npm --save redux React-redux

W pliku index.js zaimportuj dostawcę z React-Redux, a swój sklep z ./store:

import {Provider} z 'React-redux';
importuj sklep z „./store”;

Zawiń komponent w przekazanym magazynie wokół w index.jst, aby podłączyć swoją aplikację do sklepu Redux:

ReactDOM.render (

    
      
    
 ,
document.getElementById („root”)
);

(Pamiętaj, że mam również , ale to jest w innym poście o tym, jak zastosowałem internacjonalizację i lokalizację, aby dynamicznie renderować treść na stronie Siren Apparel. Inna historia na inny dzień.)

Teraz oczywiście nie utworzyliśmy jeszcze pliku ./store.js. Utwórz swój sklep w store.jsin src / root i umieść w nim:

import {createStore} z 'redux';
import reduktora z „./reducers/cart”;
eksportuj domyślne createStore (reduktor);

Utwórz plik reduktorów w pliku src / reduktory / cart.js i wklej ten kod:

// stan początkowy
const initState = {
  isCartOpen: false,
  kasa: {lineItems: []},
  produkty: [],
  sklep: {}
}
// działania
const CLIENT_CREATED = 'CLIENT_CREATED'
const PRODUCTS_FOUND = 'PRODUCTS_FOUND'
const CHECKOUT_FOUND = 'CHECKOUT_FOUND'
const SHOP_FOUND = 'SHOP_FOUND'
const ADD_VARIANT_TO_CART = 'ADD_VARIANT_TO_CART'
const UPDATE_QUANTITY_IN_CART = 'UPDATE_QUANTITY_IN_CART'
const REMOVE_LINE_ITEM_IN_CART = 'REMOVE_LINE_ITEM_IN_CART'
const OPEN_CART = 'OPEN_CART'
const CLOSE_CART = 'CLOSE_CART'
// reduktory
eksportuj domyślnie (stan = initState, akcja) => {
  przełącznik (typ działania) {
    sprawa CLIENT_CREATED:
      return {... stan, klient: action.payload}
    skrzynka PRODUCTS_FOUND:
      return {... stan, produkty: action.payload}
    przypadek CHECKOUT_FOUND:
      return {... stan, kasa: action.payload}
    sprawa SHOP_FOUND:
      return {... stan, sklep: action.payload}
    sprawa ADD_VARIANT_TO_CART:
      return {... state, isCartOpen: action.payload.isCartOpen, checkout: action.payload.checkout}
    sprawa UPDATE_QUANTITY_IN_CART:
      return {... stan, kasa: action.payload.checkout}
    sprawa REMOVE_LINE_ITEM_IN_CART:
      return {... stan, kasa: action.payload.checkout}
    sprawa OPEN_CART:
      return {... state, isCartOpen: true}
    sprawa CLOSE_CART:
      return {... state, isCartOpen: false}
    domyślna:
      stan powrotu
  }
}

Nie martw się, nie zamierzam po prostu publikować tego dużego reduktora i nie dyskutować o tym, co się dzieje; dojdziemy do każdego wydarzenia! Warto zwrócić uwagę na kilka rzeczy.

Bierzemy stan początkowy z tego, co zapisano, jak w przykładzie Shopify GitHub, i umieszczamy go w naszym initState, a mianowicie następujące cztery części stanu:

isCartOpen: false,
kasa: {lineItems: []},
produkty: [],
sklep: {}

Jednak w mojej implementacji tworzę również część stanu klienta. Wywołuję funkcję createClient () raz, a następnie natychmiast ustawiam ją w stanie Redux w index.js. Przejdźmy do index.js:

Powrót do index.js

const client = Client.buildClient ({
  storefrontAccessToken: „twój-shopify-token”,
  domena: „twoja-shopify-url.myshopify.com”
});
store.dispatch ({type: 'CLIENT_CREATED', ładunek: klient});

W przykładzie Kup SDK Shopify, istnieje kilka wywołań asynchronicznych, aby uzyskać informacje o produktach i przechowywać informacje w funkcji componentWillMount () Reacta. Ten przykładowy kod wygląda następująco:

componentWillMount () {
    this.props.client.checkout.create (). następnie ((res) => {
      this.setState ({
        kasa: res,
      });
    });
this.props.client.product.fetchAll (). następnie ((res) => {
      this.setState ({
        produkty: res,
      });
    });
this.props.client.shop.fetchInfo (). następnie ((res) => {
      this.setState ({
        sklep: res,
      });
    });
  }

Zdecydowałem się to zrobić zamiast tego jak najdalej w górę ładowania strony, bezpośrednio w index.js. Następnie wydałem odpowiednie zdarzenie, gdy otrzymano każdą część odpowiedzi:

// buildClient () jest synchroniczny, więc możemy to wszystko wywołać po!
client.product.fetchAll (). then ((res) => {
  store.dispatch ({type: 'PRODUCTS_FOUND', ładunek: res});
});
client.checkout.create (). then ((res) => {
  store.dispatch ({type: 'CHECKOUT_FOUND', ładunek: res});
});
client.shop.fetchInfo (). then ((res) => {
  store.dispatch ({type: 'SHOP_FOUND', ładunek: res});
});

Do tej pory został utworzony reduktor, a inicjalizacja klienta API Shopify została zakończona dla index.js.

Powrót do App.js

Teraz w App.js, podłącz sklep Redux do stanu aplikacji:

import {connect} z 'React-redux';

i nie zapomnij również zaimportować sklepu:

importuj sklep z „./store”;

Na dole, gdzie powinna znajdować się domyślna aplikacja eksportu, zmodyfikuj ją w następujący sposób:

eksportuj domyślne połączenie ((stan) => stan) (aplikacja);

To łączy stan Redux ze składnikiem aplikacji.

Teraz w funkcji render () jesteśmy w stanie uzyskać dostęp do stanu Redux za pomocą metody getState () Redux (stosowanej w przypadku waniliowego reagowania this.state):

renderowanie() {
    ...
    const state = store.getState ();
}

Wreszcie: moduły obsługi zdarzeń (wciąż jesteśmy w App.js)

Z góry wiadomo, że w App.js potrzebujemy tylko trzech procedur obsługi zdarzeń, ponieważ koszyk używa tylko trzech: updateQuantityInCart, removeLineItemInCart i handleCartClose. Oryginalne moduły obsługi zdarzeń koszyka z przykładowego repozytorium GitHub, które korzystały ze stanu lokalnego składnika, wyglądały tak:

updateQuantityInCart (lineItemId, ilość) {
  const checkoutId = this.state.checkout.id
  const lineItemsToUpdate = [{id: lineItemId, ilość: parseInt (ilość, 10)}]
zwraca this.props.client.checkout.updateLineItems (checkoutId, lineItemsToUpdate). następnie (res => {
    this.setState ({
      kasa: res,
    });
  });
}
removeLineItemInCart (lineItemId) {
  const checkoutId = this.state.checkout.id
zwraca this.props.client.checkout.removeLineItems (checkoutId, [lineItemId]). następnie (res => {
    this.setState ({
      kasa: res,
    });
  });
}
handleCartClose () {
  this.setState ({
    isCartOpen: false,
  });
}

Możemy je refaktoryzować w celu wysłania zdarzeń do sklepu Redux w następujący sposób:

updateQuantityInCart (lineItemId, ilość) {
    const state = store.getState (); // stan ze sklepu redux
    const checkoutId = state.checkout.id
    const lineItemsToUpdate = [{id: lineItemId, ilość: parseInt (ilość, 10)}]
    state.client.checkout.updateLineItems (checkoutId, lineItemsToUpdate). następnie (res => {
      store.dispatch ({type: 'UPDATE_QUANTITY_IN_CART', ładunek: {Checkout: res}});
    });
}
removeLineItemInCart (lineItemId) {
    const state = store.getState (); // stan ze sklepu redux
    const checkoutId = state.checkout.id
    state.client.checkout.removeLineItems (checkoutId, [lineItemId]). następnie (res => {
      store.dispatch ({type: 'REMOVE_LINE_ITEM_IN_CART', ładunek: {kasy: res}});
    });
}
handleCartClose () {
    store.dispatch ({type: 'CLOSE_CART'});
}
handleCartOpen () {
    store.dispatch ({type: 'OPEN_CART'});
}

Jeśli podążałeś za mną, już wspomniałem, że dodałem własną funkcję handleCartOpen, ponieważ przekazuję tę funkcję jako rekwizyt do mojego komponentu