Detaljan vodič: kako koristiti Shopifyjev API Storefront s Reactom i Reduxom

E-trgovina za sve! (... web stranice, to je )

Napisao Chris August 2018, ažurirano studeni 2018

Ljubaznošću negativnog prostora na pexels.com

Pozadina i motivacija

Dakle, motivacija je ovdje bila prilično jednostavna. Želio sam da posjetitelji moje web lokacije mogu pregledavati, pretraživati ​​i birati proizvode izravno na mojoj prilagođenoj domeni bez potrebe da odu na našu Shopify stranicu.

Sekundarna motivacija je ta što bih radije imao vlastitu bazu kodova za web mjesto nego da koristim jedan od tvorničkih predložaka Shopifyja. Bez uvrede Shopify team! Predlošci su moderni i čisti, ali su prilično osnovni. Siguran sam da su ti predlošci lako prilagodljivi, ali to trenutno nije snop.

Dakle, ovo je najbolje iz oba svijeta - moja prilagođena stranica React (već izgrađena i online ), s dodanim API-jem i postupkom odjave Shopifyja!

Na kraju ovog vodiča moći ćete dodati svoje Shopify proizvode na bilo koju stranicu vaše web lokacije. Jedini dio procesa kupovine koji će se dogoditi na Shopify je kada korisnik klikne "Checkout".

Napravio sam i prazno spremište ormara za ovu poduku.

Motivacija posebno za pisanje ovdje na Medijumu bila je jednostavno ta što nisam mogao pronaći udžbenik o ovom procesu - pa sam ga odlučio napraviti!

Ja sam profesionalni programer već 4 godine, a programirao sam 7. Radio sam u tehničkim paketima od old school Fortrana i Perla, do React, Javascript, Python i Node.

Siren Odjeća jedna je od mojih sporednih tvrtki / pokretača / proizvođača proizvoda koju vodim već 5 godina, a do sada smo donirali pet različitih policijskih i vatrogasnih službi!

Počnimo s ovim vodičem.

Shopify's API Storefront

Prekrasni ljudi iz Shopifyja sastavili su Storefront API. Pomoću Storefront API-ja možete stvoriti komponente React da biste dodali slike proizvoda, varijacije proizvoda, veličine proizvoda, košaricu i gumbe za dodavanje u košaricu i 'checkout' na svoje web mjesto koje nije Shopify.

* Imajte na umu da ovaj vodič nije o Shopify Polarisu koji se koristi za stvaranje komponenata u samom upravljanju prodavaonicom React for Shopify.

Početak rada: react-js-buy Repository

Pogledajte ovaj React primjer koji je izgradio Shopify tim. Većina koda u ovom vodiču potječe iz tog spremišta.

... Jeste li pogledali? Dobro!

Sad idemo skok pravo u kod! Krenite do korijenske mape vašeg web mjesta React i instalirajte modul shopify-buy preko terminala:

cd my-awesome-react-project /
npm install - uštedjeti kupi-kupi

(ili dodajte predivu kupite-kupite ako preferirate pređu)

Zatim ćete u svom sučelju index.js (NE App.js!) Trebati uvoziti klijenta iz SDK-a JS Buy:

uvoziti klijenta iz 'shopify-buy';

Zatim iznad poziva ReactDOM.render () dodajte sljedeći objekt konfiguracije:

const client = Client.buildClient ({
    storefrontAccessToken: 'vaš pristupni token',
    domena: 'your-shopify-url.myshopify.com'
});

To je to za index.js za sada - brzo ćemo mu se vratiti.

Sada ćemo dodati sve komponente potrebne za nesmetano iskustvo kupovine i kupnje. Kopirajte sve komponente iz spremišta reakct-js-buy:

Cart.js

LineItem.js

Product.js

Products.js

VariantSelector.js

Te ćemo dijelove zalijepiti u acomponents / shopify / mapu u vaš src / folder. Ove komponentne datoteke možete staviti bilo gdje drugdje u src / folder, ako želite. Ostatak udžbenika pretpostavlja da ste ih stavili u komponente / shopify /.

Izmjena App.js

App.js će trebati opsežne izmjene. Prvo uvezite komponentu Košara koju ste upravo kopirali u vlastiti projekt:

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

Ako je vaša komponenta App.js bila bez stanja, poput moje, trebali biste sigurno kopirati cijelu ovu funkciju konstruktora ():

constructor () {
    super();
    this.updateQuantityInCart = this.updateQuantityInCart.bind (ovo);
    this.removeLineItemInCart = this.removeLineItemInCart.bind (ovo);
    this.handleCartClose = this.handleCartClose.bind (ovo);
}

Ako već imate stanje, kopirajte samo one retke veze. Ta su tri retka funkcije priređivača događaja za koje Shopify košaricu treba pravilno funkcionirati.

"Ali što je s državom za košaricu?"

Možete pitati; ili:

"Što je s definiranjem onih koji će upravljati događajima za košaricu!"

Zaista, to dolazi, ali ne još!

Zatim možete dodati komponentu na dno svoje funkcije render () prije završnog div.

Po mom mišljenju, košarica bi trebala biti dostupna bilo gdje u vašoj aplikaciji. Mislim da onda ima smisla komponentu staviti u korijensku komponentu vaše aplikacije - drugim riječima, App.js:

povratak (
... );

Opet, još nisam uključio nijedan kôd u program za obradu događaja za košaricu. Uz to, nisam se obratio nedostatku državnih komponenti za košaricu u App.js.

Za to postoji dobar razlog.

Otprilike na pola projekta, shvatio sam da se moja komponenta proizvoda, naravno, ne nalazi u datoteci App.js.

Umjesto toga pokopano je oko tri komponente djece.

Dakle, umjesto da proizvode proslijedite u tri razine, a zatim radite sa alatima za rukovanje sve do sigurnosne kopije ...

Odlučio sam se poslužiti…

Redux !!!

Uh! Znam, znam da, Redux, iako nije vrlo težak, predstavlja bol u% * $! u početku se spojiti sa svim potrebnim kotlovskim pločama. Ali, ako ste programer koji radi u trgovini e-trgovine ili ste vlasnik e-trgovine, razmislite o tome na sljedeći način: Redux će vam omogućiti pristup stanju košarice s bilo koje komponente ili stranice na našoj web stranici ili webapp-u.

Ova će sposobnost biti bitna jer se Sirena odjeća širi i razvijamo više proizvoda. Kako stvaramo više proizvoda, napravit ću zasebnu stranicu posvećene trgovini sa svim proizvodima, a na početnoj stranici ostavljam samo nekoliko istaknutih proizvoda.

Sposobnost pristupa košarici je ključna ako korisnik kupuje malo, čita neke priče ili podatke o odjeći Siren, a zatim se odluči na kupnju. Nije važno koliko se kreću, ništa od kolica neće biti izgubljeno!

Ukratko, zaključio sam da je vjerojatno bolje implementirati Redux sada, dok baza koda za našu web lokaciju nije prevelika.

Uvođenje Reduxa za Shopify Kupi SDK s minimalnim grijaćim grijačem

Instalirajte NPM pakete redux i react-redux:

npm install - spremanje redux react-redux

U index.js uvezite Dobavljača iz react-redux i vaše trgovine iz ./store:

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

Omotajte komponentu sa spremljenom prodavaonicom oko vašeg u indeksu.jsto povežite svoju aplikaciju u vašu Redux trgovinu:

ReactDOM.render (

    
      
    
 
document.getElementById ( 'korijen')
);

(Imajte na umu da i ja imam , ali to je u drugom postu o tome kako sam primijenio internacionalizaciju i lokalizaciju kako bih dinamički prikazivao sadržaj na web lokaciji Sirena odjeća. Drugačija priča za drugi dan.)

Sada, naravno, još nismo napravili ./store.js datoteku. Stvorite svoju trgovinu u store.jsin src / root i stavite to u nju:

import {createStore} iz 'redux';
reduktor uvoza iz './reducers/cart';
izvoz zadani createStore (reduktor);

Stvorite svoju datoteku reduktora u src / reduktor / cart.js i zalijepite ovaj kod:

// početno stanje
const initState = {
  isCartOpen: lažno,
  odjava: {lineItems: []},
  proizvodi: [],
  trgovina: {}
}
// radnje
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'
// reduktori
izvoz zadano (stanje = initState, akcija) => {
  sklopka (vrsta radnje) {
    slučaj CLIENT_CREATED:
      return {... država, klijent: action.payload}
    slučaj PRODUCTS_FOUND:
      return {... država, proizvodi: action.payload}
    slučaj CHECKOUT_FOUND:
      povratak {... država, odjava: action.payload}
    slučaj SHOP_FOUND:
      povratak {... država, trgovina: action.payload}
    futrola ADD_VARIANT_TO_CART:
      povratak {... stanje, isCartOpen: action.payload.isCartOpen, naplata: action.payload.checkout}
    slučaj UPDATE_QUANTITY_IN_CART:
      povratak {... država, odjava: action.payload.checkout}
    slučaj REMOVE_LINE_ITEM_IN_CART:
      povratak {... država, odjava: action.payload.checkout}
    futrola OPEN_CART:
      return {... stanje, isCartOpen: true}
    futrola CLOSE_CART:
      return {... stanje, isCartOpen: false}
    zadano:
      povratno stanje
  }
}

Ne brinite, neću objaviti ovaj veliki reduktor i ne raspravljati o onome što se događa; doći ćemo do svakog događaja! Ovdje treba napomenuti nekoliko stvari.

Uzimamo početno stanje iz onoga što je stanje napisano kao u primjeru Shopify GitHub i stavljamo ga u našu initState, naime sljedeća četiri dijela stanja:

isCartOpen: lažno,
odjava: {lineItems: []},
proizvodi: [],
trgovina: {}

Međutim, u svojoj provedbi stvaram i klijentski dio države. Jednom nazovem createClient () funkciju, a zatim je odmah postavim u stanje Redux u index.js. Pa krenimo u index.js:

Natrag na index.js

const client = Client.buildClient ({
  storefrontAccessToken: 'vaš-shopify-token',
  domena: 'your-shopify-url.myshopify.com'
});
store.dispatch ({vrsta: 'CLIENT_CREATED', korisni teret: klijent});

U primjeru Shopify buy SDK-a postoji nekoliko poziva za asinkrovanje radi dobivanja podataka o proizvodima i spremanja podataka u React-ovu komponentu komponenta WillMount (). Taj primjer izgleda ovako:

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

Odlučio sam se na to umjesto što dalje od učitavanja web mjesta, izravno u index.js. Zatim sam priredio odgovarajući događaj kad je primljen svaki dio odgovora:

// buildClient () je sinkroni, tako da sve to možemo nazvati nakon!
client.product.fetchAll (). tada ((res) => {
  store.dispatch ({vrsta: 'PRODUCTS_FOUND', korisni teret: res});
});
client.checkout.create (). zatim ((res) => {
  store.dispatch ({vrsta: 'CHECKOUT_FOUND', korisni teret: res});
});
client.shop.fetchInfo (). tada ((res) => {
  store.dispatch ({vrsta: 'SHOP_FOUND', korisni teret: res});
});

Do sada je stvoren reduktor, a inicijalizacija Shopify API klijenta je završena sve za index.js.

Natrag na App.js

Sada u App.js, povežite Reduxovu trgovinu do stanja aplikacije:

import {connect} iz 'react-redux';

i ne zaboravite uvesti trgovinu:

import store iz './store';

Na dnu mjesta na kojem bi trebala biti izvozna zadana aplikacija modificirajte to:

izvesti zadano povezivanje ((stanje) => stanje) (aplikacija);

Ovo povezuje stanje Redux sa komponentom aplikacije.

Sada smo u funkciji render () u mogućnosti da pristupimo Reduxovoj državi pomoću Reduxovog getState () (kao što je postavljeno pomoću ovog stanja države vanilije)

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

Konačno: Događaji događaja (još uvijek smo u App.js)

Odozgo, znate da u App.js-u trebamo samo tri alata za obradu događaja jer košarica koristi samo tri: updateQuantityInCart, removeLineItemInCart i handleCartClose. Izvorni poslužitelji događaja košarice iz primjera spremišta GitHub, koji koristi lokalno stanje komponenata, izgledali su ovako:

updateQuantityInCart (lineItemId, količina) {
  const checkoutId = this.state.checkout.id
  const lineItemsToUpdate = [{id: lineItemId, količina: parseInt (količina, 10)}]
vratite this.props.client.checkout.updateLineItems (checkoutId, lineItemsToUpdate) .then (res => {
    this.setState ({
      odjava: res,
    });
  });
}
ukloniLineItemInCart (lineItemId) {
  const checkoutId = this.state.checkout.id
vratite ovo.props.client.checkout.removeLineItems (checkoutId, [lineItemId]). Zatim (res => {
    this.setState ({
      odjava: res,
    });
  });
}
handleCartClose () {
  this.setState ({
    isCartOpen: lažno,
  });
}

Možemo ih obnoviti za slanje događaja u Redux trgovini na sljedeći način:

updateQuantityInCart (lineItemId, količina) {
    const state = store.getState (); // stanje 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', korisni teret: {checkout: res}});
    });
}
ukloniLineItemInCart (lineItemId) {
    const state = store.getState (); // stanje iz trgovine redux
    const checkoutId = state.checkout.id
    state.client.checkout.removeLineItems (checkoutId, [lineItemId]). tada (res => {
      store.dispatch ({vrsta: 'REMOVE_LINE_ITEM_IN_CART', korisni teret: {checkout: res}});
    });
}
handleCartClose () {
    store.dispatch ({vrsta: 'CLOSE_CART'});
}
handleCartOpen () {
    store.dispatch ({vrsta: 'OPEN_CART'});
}

Ako slijedite dalje, već sam spomenuo da sam svojoj funkciji handleCartOpen dodao svoju funkciju, jer tu funkciju prenosim kao pomoćnik mojoj