Podrobna vadnica: kako uporabiti API Shopify's Storefront z React in Redux

E-trgovina za vse! (… Spletna mesta, to je )

Spisal Chris August 2018, posodobljeno november 2018

Z dovoljenjem negativnega prostora na pexels.com

Ozadje in motivacija

Torej je bila motivacija tukaj precej preprosta. Želel sem si, da bi obiskovalci mojega spletnega mesta lahko brskali, iskali in izbirali izdelke neposredno na moji domeni po meri, ne da bi morali obiskati našo stran Shopify.

Sekundarna motivacija je, da bi raje imel svojo kodno bazo za spletno mesto, kot da bi uporabil eno od tovarniških predloga Shopify. Brez zamere Shopify team! Predloge so moderne in čiste, vendar so precej osnovne. Prepričan sem, da so te predloge zelo prilagodljive, vendar to trenutno ni sklad.

To je najboljše iz obeh svetov - moje prilagojeno spletno mesto React (že zgrajeno in na spletu ), z dodanim API-jem in postopkom nakupa Shopify!

Na koncu te vadbe boste svoje izdelke Shopify lahko dodali na kateri koli strani svojega spletnega mesta. Edini del nakupovalnega procesa, ki se bo zgodil pri Shopify, je, ko uporabnik klikne »Naročilo«.

Za to vadnico sem ustvaril tudi prazen repozitorij kotne plošče.

Motivacija posebej za pisanje tukaj na Mediumu je bila preprosto ta, da sama nisem mogla najti vadbe o tem procesu - zato sem se odločila, da jo naredim!

Že 4 leta sem profesionalni razvijalec in programiram 7. Delal sem v tehnoloških sklopih od starih šol Fortran in Perl, do React, Javascript, Python in Node.

Siren Apparel je eno od mojih stranskih projektov / podjetij za zagon / izdelovalce, ki ga vodim že 5 let, in smo doslej podarili petim različnim policijskim in gasilskim oddelkom!

Začnimo s to vadnico.

Shopify's API za trgovino

Čudoviti ljudje v Shopifyju so sestavili API Storefront. Z API-jem Storefront lahko ustvarite komponente React, da dodate slike izdelka, različice izdelka, velikosti izdelka, voziček in gumbe »dodaj v košarico« in gumbe za »nakup« na svoje spletno mesto, ki ni Shopify.

* Upoštevajte, da ta vadnica NI o Shopify Polaris, ki se uporablja za ustvarjanje komponent v samem upravljanju trgovine React for Shopify.

Uvod: odzivnik-reak-buy Repository

Oglejte si ta primer React, ki ga je zgradila ekipa Shopify. Večina kode v tej vadnici prihaja iz tega skladišča.

... Ste si ga ogledali? Dobro!

Zdaj bomo prešli v kodo! Pojdite v korensko mapo svojega spletnega mesta React in preko terminala namestite modul shopify-buy:

cd my-awesome-react-project /
npm namestite - prihranite pri nakupu

(ali preje dodajte shopify-buy, če vam je ljubša preja)

Nato morate v svoj sprednji indeks.js (NE App.js!) Uvažati odjemalca iz SDK-ja za nakup JS:

uvozi odjemalca iz 'shopify-buy';

Nato nad klicom ReactDOM.render () dodajte naslednji konfiguracijski objekt:

const odjemalec = Client.buildClient ({
    storefrontAccessToken: 'žeton vašega dostopa',
    domena: 'your-shopify-url.myshopify.com'
});

To je zdaj za index.js - k njemu se bomo kmalu vrnili.

Zdaj bomo dodali vse komponente, potrebne za nemoteno nakupovanje in nakupovanje. Kopirajte vse komponente iz shrambe reakct-js-buy:

Cart.js

LineItem.js

Product.js

Products.js

VariantSelector.js

Te komponente bomo prilepili v acomponents / shopify / mapo v vašem src / folder. Te datoteke komponent lahko po želji postavite kam drugam v src / mapo. Preostali del vadnice predvideva, da ste jih vdeli v komponente / shopify /.

Spreminjanje App.js

App.js bo potreboval obsežne spremembe. Najprej uvozite tisto komponento Košarice, ki ste jo pravkar kopirali v svoj lastni projekt:

uvoz Košarica iz './components/shopify/Cart';

Če je bila vaša komponenta App.js brez stanja, kot je moja, morate varno kopirati to celotno funkcijo konstruktorja ():

konstruktor () {
    super ();
    this.updateQuantityInCart = this.updateQuantityInCart.bind (to);
    this.removeLineItemInCart = this.removeLineItemInCart.bind (to);
    this.handleCartClose = this.handleCartClose.bind (to);
}

Če že imate stanje, kopirajte samo tiste vrstice vezave. Te tri vrstice so funkcije prirejevalca dogodkov, za katere voziček Shopify mora pravilno delovati.

"Kaj pa stanje za voziček!"

Boste morda vprašali; ali:

"Kaj pa določiti tiste upravljavce dogodkov za voziček!"

Dejansko to prihaja, a še ne!

Nato lahko komponento dodate na dno funkcije render () pred končnim delom.

Po mojem mnenju mora biti voziček dostopen kjer koli v vaši aplikaciji. Po mojem mnenju je smiselno komponento vstaviti v korensko komponento vaše aplikacije - z drugimi besedami, App.js:

vrnitev (
...
);

Ponovno še nisem vključil nobene kode v upravljavce dogodkov za voziček. Poleg tega nisem obravnaval pomanjkanja državnih komponent za voziček v App.js.

Za to obstaja dober razlog.

Približno na polovici tega projekta sem ugotovil, da komponent izdelkov seveda ni v datoteki App.js.

Namesto tega so jo pokopali približno trije otroški sestavni deli.

Torej, namesto da bi izdelke posredovali na treh nivojih navzdol otrokom, nato pa funkcijske rokovalnike naredili do konca ...

Odločila sem se uporabiti…

Redux !!!

Uh! Vem, vem, Redux, čeprav ni zelo težaven, je bolečina v% * $! da se najprej priključite na vse potrebne plošče kotla. Če pa ste razvijalec, ki deluje v trgovini za e-trgovino ali lastnik trgovine z e-trgovino, si omislite to: Redux vam bo omogočil dostop do stanja košarice s katere koli komponente ali strani na našem spletnem mestu ali webapp-u.

Ta sposobnost bo bistvenega pomena, saj se Siren Apparel širi in razvijamo več izdelkov. Ko ustvarjamo več izdelkov, bom z vsemi izdelki ustvaril ločeno namensko stran trgovine, na domači strani pa bom pustil le peščico predstavljenih izdelkov.

Možnost dostopa do vozička je bistvenega pomena, če uporabnik nakupuje, prebere nekaj zgodb ali informacij o oblačilih Siren in se nato odloči za nakup. Ni važno, koliko potujejo, ničesar iz vozička ne bo izgubljeno!

Torej, skratka, odločil sem se, da je verjetno bolje, da Redux zdaj implementiramo, medtem ko zbirka podatkov za naše spletno mesto ni prevelika.

Izvedba programa Redux za Shopify Buy SDK z minimalno minimalno ogrevalno ploščo

Namestite NPM pakete redux in react-redux:

npm install - shranite redux react-redux

V index.js uvozite ponudnika iz react-redux in iz trgovine ./store:

import {Provider} iz 'react-redux';
uvoz trgovina iz './store';

Omogočite komponento s poslano trgovino okoli svojega v indeksu.jsto, da priklopite svojo aplikacijo v svojo trgovino Redux:

ReactDOM.render (

    
      
    
 ,
document.getElementById ('root')
);

(Upoštevajte, da imam tudi , vendar je to v drugem prispevku o tem, kako sem uporabil internacionalizacijo in lokalizacijo za dinamično upodabljanje vsebine na spletnem mestu Siren Apparel. Drugačna zgodba za drug dan.)

Zdaj seveda še nismo naredili ./store.js datoteke. Ustvarite svojo trgovino v store.jsin src / root in jo vstavite v to:

import {createStore} iz 'redux';
uvoznik reduktor iz './reducers/cart';
izvoziti privzeti createStore (reducer);

Ustvarite svojo redukcijsko datoteko v src / reducer / cart.js in prilepite to kodo:

// začetno stanje
const initState = {
  isCartOpen: napačno,
  blagajna: {lineItems: []},
  izdelki: [],
  trgovina: {}
}
// dejanja
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'
// reduktorji
izvozno privzeto (stanje = initState, dejanje) => {
  stikalo (action.type) {
    primer CLIENT_CREATED:
      return {... država, odjemalec: action.payload}
    primer PRODUCTS_FOUND:
      return {... država, izdelki: action.payload}
    primer CHECKOUT_FOUND:
      return {... država, blagajna: action.payload}
    primer SHOP_FOUND:
      vrnitev {... država, trgovina: action.payload}
    primer ADD_VARIANT_TO_CART:
      vrne {... stanje, isCartOpen: action.payload.isCartOpen, checkout: action.payload.checkout}
    Primer UPDATE_QUANTITY_IN_CART:
      return {... država, blagajna: action.payload.checkout}
    primer REMOVE_LINE_ITEM_IN_CART:
      return {... država, blagajna: action.payload.checkout}
    Primer OPEN_CART:
      return {... stanje, isCartOpen: true}
    Primer CLOSE_CART:
      return {... stanje, isCartOpen: false}
    privzeto:
      povratno stanje
  }
}

Brez skrbi, ne bom objavil tega velikega reduktorja in ne razpravljal o tem, kaj se dogaja; prišli bomo do vsakega dogodka! Tu je treba opozoriti nekaj stvari.

Začetno stanje vzamemo iz tega, kar je stanje zapisano kot v primeru Shopify GitHub, in ga vstavimo v naš initState, in sicer v naslednje štiri dele stanja:

isCartOpen: napačno,
blagajna: {lineItems: []},
izdelki: [],
trgovina: {}

Vendar pri svoji implementaciji ustvarim tudi strankin del države. Enkrat pokličem funkcijo createClient () in jo takoj nastavim v stanje Redux v index.js. Pojdimo torej v index.js:

Nazaj na index.js

const odjemalec = Client.buildClient ({
  storefrontAccessToken: 'svoj-shopify-token',
  domena: 'your-shopify-url.myshopify.com'
});
store.dispatch ({vrsta: 'CLIENT_CREATED', uporabna obremenitev: odjemalec});

V primeru SDK za nakup Shopify kupi nekaj async klicev za pridobivanje informacij o izdelkih in shranjevanje informacij v funkciji React komponentaWillMount (). Ta primer koda izgleda tako:

komponentaWillMount () {
    this.props.client.checkout.create (). potem ((res) => {
      this.setState ({
        odjava: res,
      });
    });
this.props.client.product.fetchAll (). potem ((res) => {
      this.setState ({
        izdelki: res,
      });
    });
this.props.client.shop.fetchInfo (). potem ((res) => {
      this.setState ({
        trgovina: res,
      });
    });
  }

Odločil sem se, da bom to storil namesto kolikor je mogoče obremenitve spletnega mesta neposredno v index.js. Nato sem objavil ustrezen dogodek, ko je bil prejet vsak del odgovora:

// buildClient () je sinhroni, zato lahko vse to pokličemo po tem!
client.product.fetchAll (). potem ((res) => {
  store.dispatch ({vrsta: 'PRODUCTS_FOUND', uporabna obremenitev: res});
});
client.checkout.create (). potem ((res) => {
  store.dispatch ({vrsta: 'CHECKOUT_FOUND', uporabna obremenitev: res});
});
client.shop.fetchInfo (). potem ((res) => {
  store.dispatch ({vrsta: 'SHOP_FOUND', uporabna obremenitev: res});
});

Zdaj je ustvarjen reduktor in inicializacija odjemalca Shopify API je za index.js končana.

Nazaj na App.js

Zdaj v App.js povežite prodajalno Redux v stanje aplikacije:

import {connect} iz 'react-redux';

in ne pozabite uvoziti tudi trgovine:

uvoz trgovina iz './store';

Na dnu, kjer naj bo izvozna privzeta aplikacija, jo spremenite tako:

izvoziti privzeto povezovanje ((stanje) => stanje) (aplikacija);

To poveže stanje Redux s komponento App.

Zdaj v funkciji render () lahko dostopamo do stanja Reduxa z Redux's getState () (kot je uporabljeno to stanje države vanilijeve reakcije):

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

Končno: prireditelji dogodkov (še vedno smo v App.js)

Zgoraj veste, da v App.jsu potrebujemo samo tri obdelovalce dogodkov, saj voziček uporablja samo tri: updateQuantityInCart, removeLineItemInCart in handleCartClose. Izvirni obdelovalci dogodkov v vozičku iz primera skladišča GitHub, ki je uporabljal lokalno stanje komponent, so izgledali tako:

updateQuantityInCart (lineItemId, količina) {
  const checkoutId = this.state.checkout.id
  const lineItemsToUpdate = [{id: lineItemId, količina: parseInt (količina, 10)}]
vrni to.props.client.checkout.updateLineItems (checkoutId, lineItemsToUpdate) .then (res => {
    this.setState ({
      odjava: res,
    });
  });
}
removeLineItemInCart (lineItemId) {
  const checkoutId = this.state.checkout.id
vrni to.props.client.checkout.removeLineItems (checkoutId, [lineItemId]). Nato (res => {
    this.setState ({
      odjava: res,
    });
  });
}
handleCartClose () {
  this.setState ({
    isCartOpen: napačno,
  });
}

Znova jih lahko obnovimo, da dogodke pošljejo v prodajalno Redux na naslednji način:

updateQuantityInCart (lineItemId, količina) {
    const state = store.getState (); // država iz trgovine redux
    const checkoutId = state.checkout.id
    const lineItemsToUpdate = [{id: lineItemId, količina: parseInt (količina, 10)}]
    state.client.checkout.updateLineItems (checkoutId, lineItemsToUpdate) .then (res => {
      store.dispatch ({vrsta: 'UPDATE_QUANTITY_IN_CART', uporabna obremenitev: {checkout: res}});
    });
}
removeLineItemInCart (lineItemId) {
    const state = store.getState (); // država iz trgovine redux
    const checkoutId = state.checkout.id
    state.client.checkout.removeLineItems (checkoutId, [lineItemId]). Nato (res => {
      store.dispatch ({vrsta: 'REMOVE_LINE_ITEM_IN_CART', uporabna obremenitev: {checkout: res}});
    });
}
handleCartClose () {
    store.dispatch ({vrsta: 'CLOSE_CART'});
}
handleCartOpen () {
    store.dispatch ({vrsta: 'OPEN_CART'});
}

Če ste nadaljevali, sem že omenil, da sem lastno funkcijo handleCartOpen dodal, ker to funkcijo prenašam kot podpornik komponenti