Ausführliches Tutorial: Verwendung der Storefront-API von Shopify mit React und Redux

E-Commerce für alle! (… Websites, das ist )

Geschrieben von Chris August 2018, aktualisiert November 2018

Mit freundlicher Genehmigung von Negative Space auf pexels.com

Hintergrund und Motivation

Die Motivation hier war also ziemlich einfach. Ich wollte, dass die Besucher meiner Website Produkte direkt in meiner benutzerdefinierten Domain durchsuchen, suchen und auswählen können, ohne unsere Shopify-Website aufrufen zu müssen.

Die sekundäre Motivation ist, dass ich lieber eine eigene Codebasis für eine Website habe, als eine der Factory-Vorlagen von Shopify zu verwenden. Kein Vergehen Shopify Team! Die Vorlagen sind modern und sauber, aber sie sind eher einfach. Ich bin mir sicher, dass diese Vorlagen stark anpassbar sind, aber ich kenne sie derzeit nicht.

Dies ist also das Beste aus beiden Welten - meine benutzerdefinierte React-Site (bereits erstellt und online), mit der hinzugefügten API und dem Checkout-Prozess von Shopify!

Am Ende dieses Lernprogramms können Sie Ihre Shopify-Produkte auf jeder Seite Ihrer Website hinzufügen. Der einzige Teil des Einkaufsprozesses, der in Shopify ausgeführt wird, ist, wenn der Benutzer auf "Kasse" klickt.

Ich habe auch für dieses Tutorial ein leeres Boilerplate-Repository erstellt.

Die Motivation, hier auf Medium zu schreiben, war einfach, dass ich selbst kein Tutorial zu diesem Vorgang finden konnte - also habe ich beschlossen, eines zu machen!

Ich bin seit 4 Jahren ein professioneller Entwickler und programmiere seit 7. Ich habe in technischen Stacks von Fortran und Perl der alten Schule bis zu React, Javascript, Python und Node gearbeitet.

Siren Apparel ist eine meiner Projekt- / Start-up- / Maker-Firmen, die ich seit 5 Jahren leite, und wir haben bisher an 5 verschiedene Polizei und Feuerwehr gespendet!

Beginnen wir endlich mit diesem Tutorial.

Shopify-Storefront-API

Die wunderbaren Leute von Shopify haben die Storefront-API zusammengestellt. Mit der Storefront-API können Sie Reaktionskomponenten erstellen, um Produktbilder, Produktvarianten, Produktgrößen, einen Warenkorb sowie die Schaltflächen "Zum Warenkorb hinzufügen" und "Zur Kasse" auf Ihrer eigenen Site hinzuzufügen, die nicht zu Shopify gehört.

* Beachten Sie, dass sich dieses Tutorial NICHT mit Shopify Polaris befasst, mit dem Komponenten in React for Shopify Store Management selbst erstellt werden.

Erste Schritte: react-js-buy Repository

Schauen Sie sich dieses vom Shopify-Team erstellte React-Beispiel an. Der größte Teil des Codes in diesem Tutorial stammt aus diesem Repository.

… Hast du einen Blick darauf geworfen? Gut!

Jetzt springen wir direkt in den Code! Gehen Sie zum Stammordner Ihrer React-Site und installieren Sie das shopify-buy-Modul über das Terminal:

cd my-awesome-react-project /
npm install --save shopify-buy

(oder Garn hinzufügen shopify-buy, wenn Sie Garn bevorzugen)

Dann müssen Sie in Ihrer Frontend-Datei index.js (NICHT App.js!) Den Client aus dem JS Buy SDK importieren:

Client aus 'shopify-buy' importieren;

Fügen Sie dann das folgende Konfigurationsobjekt über dem Aufruf von ReactDOM.render () hinzu:

const client = Client.buildClient ({
    storefrontAccessToken: 'Ihr Zugriffstoken',
    domain: 'your-shopify-url.myshopify.com'
});

Das ist alles für index.js - wir werden bald darauf zurückkommen.

Jetzt fügen wir alle Komponenten hinzu, die für ein reibungsloses Einkaufs- und Checkout-Erlebnis erforderlich sind. Kopieren Sie alle Komponenten aus dem Repository von react-js-buy:

Cart.js

LineItem.js

Product.js

Products.js

VariantSelector.js

Wir werden diese Komponenten in einen Component / shopify / -Ordner in Ihrem src / -Ordner einfügen. Sie können diese Komponentendateien an einer anderen Stelle im Ordner src / ablegen, wenn Sie dies wünschen. Der Rest des Tutorials setzt voraus, dass Sie sie in components / shopify / abgelegt haben.

Ändern von App.js

App.js benötigen umfangreiche Änderungen. Importieren Sie zunächst die Cart-Komponente, die Sie gerade in Ihr eigenes Projekt kopiert haben:

Wagen importieren von './components/shopify/Cart';

Wenn Ihre App.js-Komponente wie meine zustandslos war, sollten Sie diese gesamte constructor () -Funktion sicher kopieren:

Konstrukteur() {
    Super();
    this.updateQuantityInCart = this.updateQuantityInCart.bind (this);
    this.removeLineItemInCart = this.removeLineItemInCart.bind (this);
    this.handleCartClose = this.handleCartClose.bind (this);
}

Wenn Sie bereits einen Status haben, kopieren Sie nur diese Bindungslinien. Diese drei Zeilen sind Event-Handler-Funktionen, die der Shopify-Einkaufswagen benötigt, um ordnungsgemäß zu funktionieren.

"Aber was ist mit Staat für den Wagen?"

Sie können fragen; oder:

"Was ist mit dem Definieren dieser Ereignishandler für den Einkaufswagen?"

Das kommt zwar, aber noch nicht!

Sie können dann die -Komponente an den unteren Rand Ihrer render () -Funktion anhängen, bevor das Div endet.

Meiner Meinung nach sollte der Warenkorb überall in Ihrer App verfügbar sein. Ich halte es daher für sinnvoll, die Komponente in der Stammkomponente Ihrer App zu platzieren - mit anderen Worten, App.js:

Rückkehr (
... );

Auch hier habe ich noch keinen Code in die Ereignisbehandlungsroutinen für den Warenkorb eingefügt. Außerdem habe ich den Mangel an Statuskomponenten für den Einkaufswagen in App.js nicht behoben.

Dafür gibt es gute Gründe.

Ungefähr in der Mitte dieses Projekts stellte ich fest, dass meine Produktkomponente natürlich nicht in meiner App.js-Datei enthalten war.

Stattdessen wurde es über drei Kinderkomponenten hinweg begraben.

Anstatt also Produkte drei Ebenen an Kinder weiterzugeben, und dann die Funktionshandler wieder ganz nach oben zu führen ...

Ich entschied mich für…

Redux !!!

Pfui! Ich weiß, ich weiß, Redux, obwohl es nicht sehr schwierig ist, ist ein Schmerz in der% * $! zu Beginn mit allen erforderlichen Boilerplate zu verdrahten. Wenn Sie jedoch ein Entwickler sind, der an einem E-Commerce-Geschäft oder einem E-Commerce-Geschäftsinhaber arbeitet, stellen Sie sich Folgendes vor: Mit Redux können Sie von jeder Komponente oder Seite unserer Website oder Webanwendung aus auf den Status des Einkaufswagens zugreifen.

Diese Fähigkeit wird von wesentlicher Bedeutung sein, wenn Siren Apparel expandiert und wir mehr Produkte entwickeln. Wenn wir weitere Produkte erstellen, werde ich eine separate Shop-Seite mit allen Produkten erstellen und nur eine Handvoll der vorgestellten Produkte auf der Homepage lassen.

Die Möglichkeit, auf den Einkaufswagen zuzugreifen, ist unerlässlich, wenn ein Benutzer ein wenig einkauft, einige Artikel oder Informationen zu Siren Apparel liest und sich dann zur Kasse entscheidet. Es spielt keine Rolle, wie viel sie sich bewegen, nichts von ihrem Einkaufswagen geht verloren!

Kurz gesagt, ich habe beschlossen, dass es wahrscheinlich besser ist, Redux jetzt zu implementieren, während die Codebasis für unsere Website nicht zu groß ist.

Implementierung von Redux für Shopify Buy SDK With Bare Minimum Boilerplate

Installieren Sie die NPM-Pakete redux und react-redux:

npm install --save redux react-redux

Importieren Sie in index.js den Provider von react-redux und Ihren Store von ./store:

{Provider} aus 'react-redux' importieren;
Laden aus './store' importieren;

Wickeln Sie die -Komponente mit dem übergebenen Store um Ihre in index.j, um Ihre App mit Ihrem Redux-Store zu verbinden:

ReactDOM.render (

    
      
    
 ,
document.getElementById ('root')
);

(Beachten Sie, dass ich auch einen besitze, aber in einem anderen Post darüber, wie ich Internationalisierung und Lokalisierung angewendet habe, um den Inhalt auf der Siren Apparel-Website dynamisch wiederzugeben. Eine andere Geschichte für einen anderen Tag.)

Jetzt haben wir natürlich noch keine ./store.js-Datei erstellt. Erstellen Sie Ihren Shop in store.jsin src / root und fügen Sie dies ein:

{createStore} aus 'redux' importieren;
Reduzierer aus './reducers/cart' importieren;
export default createStore (Reduzierer);

Erstellen Sie Ihre Reduziererdatei in src / reducers / cart.js und fügen Sie diesen Code ein:

// Ausgangszustand
const initState = {
  isCartOpen: false,
  checkout: {lineItems: []},
  Produkte: [],
  Geschäft: {}
}
// Aktionen
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'
// Reduzierungen
export default (state = initState, action) => {
  switch (action.type) {
    case CLIENT_CREATED:
      return {... state, client: action.payload}
    case PRODUCTS_FOUND:
      return {... state, products: action.payload}
    case CHECKOUT_FOUND:
      return {... state, checkout: action.payload}
    case SHOP_FOUND:
      return {... state, shop: action.payload}
    case ADD_VARIANT_TO_CART:
      return {... state, isCartOpen: action.payload.isCartOpen, checkout: action.payload.checkout}
    case UPDATE_QUANTITY_IN_CART:
      return {... state, checkout: action.payload.checkout}
    case REMOVE_LINE_ITEM_IN_CART:
      return {... state, checkout: action.payload.checkout}
    case OPEN_CART:
      return {... state, isCartOpen: true}
    case CLOSE_CART:
      return {... state, isCartOpen: false}
    Standard:
      Rückgabestatus
  }
}

Keine Sorge, ich werde nicht nur diesen großen Reduzierer posten und nicht diskutieren, was los ist. Wir kommen zu jeder Veranstaltung! Hier sind einige Dinge zu beachten.

Wir nehmen den Anfangszustand von dem, was der Zustand wie im Shopify GitHub-Beispiel geschrieben hat, und fügen ihn in unseren initState ein, und zwar die folgenden vier Teile des Zustands:

isCartOpen: false,
checkout: {lineItems: []},
Produkte: [],
Geschäft: {}

In meiner Implementierung erstelle ich jedoch auch einen Client-Teil des Staates. Ich rufe die Funktion createClient () einmal auf und setze sie dann sofort in index.js in den Redux-Zustand. Gehen wir also zu index.js:

Zurück zu index.js

const client = Client.buildClient ({
  storefrontAccessToken: 'your-shopify-token',
  domain: 'your-shopify-url.myshopify.com'
});
store.dispatch ({type: 'CLIENT_CREATED', payload: client});

Im Beispiel für das Shopify-Kauf-SDK gibt es einige asynchrone Aufrufe, um Informationen zu den Produkten abzurufen und Informationen in der React-Funktion componentWillMount () zu speichern. Dieser Beispielcode sieht folgendermaßen aus:

componentWillMount () {
    this.props.client.checkout.create (). then ((res) => {
      this.setState ({
        Kasse: res,
      });
    });
this.props.client.product.fetchAll (). then ((res) => {
      this.setState ({
        Produkte: res,
      });
    });
this.props.client.shop.fetchInfo (). then ((res) => {
      this.setState ({
        Geschäft: res,
      });
    });
  }

Ich habe mich dafür entschieden, dies so weit wie möglich vor einer Site-Auslastung direkt in index.js zu tun. Dann habe ich ein entsprechendes Ereignis ausgegeben, wenn jeder Teil der Antwort eingegangen ist:

// buildClient () ist synchron, also können wir all diese nachher aufrufen!
client.product.fetchAll (). then ((res) => {
  store.dispatch ({type: 'PRODUCTS_FOUND', payload: res});
});
client.checkout.create (). then ((res) => {
  store.dispatch ({type: 'CHECKOUT_FOUND', payload: res});
});
client.shop.fetchInfo (). then ((res) => {
  store.dispatch ({type: 'SHOP_FOUND', payload: res});
});

Inzwischen ist der Reduzierer erstellt und die Initialisierung des Shopify-API-Clients für index.js ist abgeschlossen.

Zurück zu App.js

Verbinden Sie nun in App.js den Redux-Store mit dem App-Status:

{connect} aus 'react-redux' importieren;

und vergessen Sie nicht, den Shop auch zu importieren:

Laden aus './store' importieren;

Ändern Sie die Standard-Export-App im unteren Bereich wie folgt:

export default connect ((state) => state) (App);

Dies verbindet den Redux-Status mit der App-Komponente.

Jetzt können wir in der render () -Funktion mit getState () von Redux auf den Status von Redux zugreifen (im Gegensatz zur Verwendung von vanilla react's this.state):

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

Endlich: die Event-Handler (wir sind noch in App.js)

Von oben wissen Sie, dass wir in App.js nur drei Ereignishandler benötigen, da der Einkaufswagen nur drei verwendet: updateQuantityInCart, removeLineItemInCart und handleCartClose. Die ursprünglichen Cart-Ereignishandler aus dem GitHub-Repository, die den lokalen Komponentenstatus verwendeten, sahen folgendermaßen aus:

updateQuantityInCart (lineItemId, Quantity) {
  const checkoutId = this.state.checkout.id
  const lineItemsToUpdate = [{id: lineItemId, Quantity: parseInt (Quantity, 10)}]
Rückgabe von this.props.client.checkout.updateLineItems (checkoutId, lineItemsToUpdate) .then (res => {
    this.setState ({
      Kasse: res,
    });
  });
}
removeLineItemInCart (lineItemId) {
  const checkoutId = this.state.checkout.id
Geben Sie this.props.client.checkout.removeLineItems (checkoutId, [lineItemId]) zurück. then (res => {
    this.setState ({
      Kasse: res,
    });
  });
}
handleCartClose () {
  this.setState ({
    isCartOpen: false,
  });
}

Wir können sie so umgestalten, dass Ereignisse wie folgt an den Redux-Store gesendet werden:

updateQuantityInCart (lineItemId, Quantity) {
    const state = store.getState (); // State from Redux Store
    const checkoutId = state.checkout.id
    const lineItemsToUpdate = [{id: lineItemId, Quantity: parseInt (Quantity, 10)}]
    state.client.checkout.updateLineItems (checkoutId, lineItemsToUpdate) .then (res => {
      store.dispatch ({type: 'UPDATE_QUANTITY_IN_CART', payload: {checkout: res}});
    });
}
removeLineItemInCart (lineItemId) {
    const state = store.getState (); // State from Redux Store
    const checkoutId = state.checkout.id
    state.client.checkout.removeLineItems (checkoutId, [lineItemId]). then (res => {
      store.dispatch ({type: 'REMOVE_LINE_ITEM_IN_CART', payload: {checkout: res}});
    });
}
handleCartClose () {
    store.dispatch ({type: 'CLOSE_CART'});
}
handleCartOpen () {
    store.dispatch ({type: 'OPEN_CART'});
}

Wenn Sie mitverfolgen, habe ich bereits erwähnt, dass ich meine eigene handleCartOpen-Funktion hinzugefügt habe, da ich diese Funktion als Requisite an meine