Tutorial terperinci: bagaimana menggunakan API Storefront Shopify dengan React dan Redux

E-dagang untuk semua! (... laman web, iaitu )

Ditulis oleh Chris August 2018, dikemaskini November, 2018

Dari segi negatif di pexels.com

Latar Belakang dan Motivasi

Jadi motivasi di sini cukup mudah. Saya mahu pelawat tapak saya dapat melayari, mencari, dan memilih produk secara langsung pada domain tersuai saya tanpa perlu pergi ke laman Shopify kami.

Motivasi sekunder adalah saya lebih suka mempunyai basis kod saya sendiri untuk laman web daripada menggunakan salah satu template kilang Shopify. Tiada kesalahan Shopify team! Templat adalah moden dan bersih, tetapi mereka agak asas. Saya pasti template-template ini sangat disesuaikan, tetapi bukan stack yang saya tahu pada masa ini.

Oleh itu, ini adalah yang terbaik dari kedua-dua dunia - tapak React custom saya (sudah dibina dan dalam talian ), dengan proses API dan proses checkout Shopify!

Pada akhir tutorial ini, anda akan dapat menambah produk Shopify anda pada mana-mana halaman laman web anda. Satu-satunya bahagian proses belanja yang akan berlaku di Shopify ialah apabila pengguna mengklik 'Pemeriksaan'.

Saya telah mencipta repositori dandang kosong untuk tutorial ini juga.

Motivasi khusus untuk menulis di sini pada Medium adalah semata-mata bahawa saya tidak dapat mencari tutorial mengenai proses ini sendiri - jadi saya memutuskan untuk membuat satu!

Saya telah menjadi pemaju profesional selama 4 tahun sekarang, dan pengaturcaraan untuk 7. Saya telah bekerja dalam tumpuan teknologi dari Fortran dan Perl sekolah lama, untuk React, Javascript, Python, dan Node.

Siren Pakaian adalah salah satu syarikat sampingan / syarikat startup / pembuat sampingan yang saya jalankan selama 5 tahun sekarang, dan kami telah menyumbangkan kepada 5 buah polis dan jabatan api yang berbeza setakat ini!

Mari kita mulakan dengan tutorial ini.

API Storefront Shopify

Orang-orang yang hebat di Shopify telah mengumpulkan API Storefront. Dengan Storefront API, anda boleh membuat komponen React untuk menambah gambar produk, variasi produk, saiz produk, gerobak, dan butang 'add to cart' dan 'checkout' ke laman web anda sendiri, non-Shopify.

* Ambil perhatian bahawa tutorial ini TIDAK mengenai Shopify Polaris, yang digunakan untuk membuat komponen dalam pengurusan React untuk Shopify sendiri.

Bermula: reaksi-js-buy Repository

Lihat contoh React ini yang dibina oleh pasukan Shopify. Kebanyakan kod dalam tutorial ini berasal dari repositori itu.

... Adakah anda melihat? Baik!

Sekarang kita akan terus masuk ke dalam kod! Pergi ke folder akar laman React anda dan pasang modul shopify-buy melalui terminal:

cd my-awesome-react-project /
npm install - menyimpan kedai beli-beli

(atau benang tambah shopify-buy jika anda lebih suka benang)

Kemudian, di index.js frontend anda, (TIDAK App.js!) Anda perlu mengimport Pelanggan dari JS Buy SDK:

import Pelanggan dari 'shopify-buy';

Kemudian tambahkan objek konfigurasi berikut di atas panggilan ReactDOM.render ():

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

Itu sahaja untuk indeks.js sekarang - kami akan kembali ke sini tidak lama lagi.

Sekarang kita akan menambah semua komponen yang diperlukan untuk pengalaman membeli-belah dan checkout yang lancar. Salin semua komponen dari repositori react-js-buy:

Cart.js

LineItem.js

Product.js

Products.js

VariantSelector.js

Kami akan memasukkan komponen ini ke dalam komponen / kedai / folder dalam src / folder anda. Anda boleh meletakkan fail komponen ini di tempat lain di src / folder, jika anda inginkan. Selebihnya tutorial mengandaikan anda telah meletakkannya dalam komponen / shopify /.

Mengubah App.js

App.js memerlukan perubahan yang mendalam. Mula-mula, import komponen Keranjang yang baru saja disalin ke dalam projek anda sendiri:

import troli dari './components/shopify/Cart';

Sekiranya komponen App.js anda tidak mempunyai stateless, seperti saya, anda harus selamat menyalin fungsi pembina keseluruhan () ini:

pembina () {
    super ();
    this.updateQuantityInCart = this.updateQuantityInCart.bind (ini);
    this.removeLineItemInCart = this.removeLineItemInCart.bind (ini);
    this.handleCartClose = this.handleCartClose.bind (ini);
}

Sekiranya anda sudah mempunyai status, salin hanya garisan pengikat tersebut. Ketiga garis itu adalah fungsi pengendali peristiwa yang diperlukan oleh Shopify cart berfungsi dengan baik.

"Tapi bagaimana pula dengan kereta !?"

Anda boleh bertanya; atau:

"Bagaimana pula dengan menentukan pengendali acara itu untuk kereta !?"

Sesungguhnya, itu akan datang, tetapi belum!

Anda kemudian boleh memasukkan komponen ke bahagian bawah fungsi render () anda, sebelum div berakhir.

Pada pendapat saya, kereta itu boleh diakses di mana-mana sahaja dalam apl anda. Saya rasa masuk akal, kemudian, untuk meletakkan komponen dalam komponen akar aplikasi anda - dengan kata lain, App.js:

kembali (
... );

Sekali lagi, saya tidak memasukkan sebarang kod pada pengendali acara untuk kereta itu lagi. Di samping itu, saya tidak menangani kekurangan komponen negara untuk kereta di App.js.

Terdapat sebab yang baik untuk ini.

Sekitar pertengahan projek ini, saya menyedari komponen produk saya sudah tentu tidak dalam fail App.js saya.

Sebaliknya, ia dikebumikan kira-kira tiga komponen kanak-kanak ke bawah.

Oleh itu, bukannya lulus produk tiga tahap ke bawah untuk kanak-kanak, dan kemudian berfungsi pengendali sepanjang jalan kembali ...

Saya memutuskan untuk menggunakan ...

Redux !!! ء

Ugh! Saya tahu, saya tahu, Redux, walaupun tidak begitu sukar, adalah sakit dalam% * $! untuk mulakan pada mulanya dengan semua boilerplate yang diperlukan. Tetapi, jika anda seorang pemaju yang bekerja di kedai E-dagang atau pemilik gedung E-dagang, fikirkannya dengan cara ini: Redux akan membolehkan anda mengakses keadaan kereta dari mana-mana komponen atau halaman di laman web atau webapp kami.

Keupayaan ini akan menjadi penting kerana Siren Apparel mengembang dan kami mengembangkan lebih banyak produk. Apabila kami membuat lebih banyak produk, saya akan membuat halaman kedai berdedikasi yang berasingan dengan semua produk, sementara hanya meninggalkan beberapa produk yang dipaparkan di halaman utama.

Keupayaan untuk mengakses kereta adalah penting sekiranya kedai pengguna di sekelilingnya, membaca beberapa cerita atau maklumat tentang Pakaian Siren, dan kemudian memutuskan untuk mendaftar keluar. Tidak kira berapa banyak mereka menavigasi, tiada apa-apa dari kereta mereka akan hilang!

Jadi, secara ringkasnya, saya memutuskan mungkin lebih baik untuk melaksanakan Redux sekarang manakala asas untuk laman web kami tidak terlalu besar.

Melaksanakan Redux untuk Shopify Buy SDK Dengan Boilerplate Minimum Bare

Pasang pakej NPM redux dan reaksi-redux:

npm install --save redux react-redux

Dalam indeks.js, Pembekal import daripada tindak balas redux dan kedai anda dari ./store:

import {Provider} dari 'react-redux';
kedai import daripada '.stst';

Balutkan komponen dengan lulus di sekeliling anda di index.jsto pasang Apl anda ke kedai Redux anda:

ReactDOM.render (

    
      
    
 ,
document.getElementById ('root')
);

(Perhatikan bahawa saya juga mempunyai , tetapi itu dalam jawatan yang berbeza tentang bagaimana saya menggunakan pengantarabangsaan dan penyetempatan untuk memberikan kandungan secara dinamik pada tapak Siren Pakaian. Cerita yang berbeza untuk hari yang berbeza.)

Kini sudah tentu kita belum membuat fail ./store.js lagi. Buat kedai anda di store.jsin src / root dan letakkan di dalamnya:

import {createStore} dari 'redux';
pengurang import daripada '.redreders/cart';
eksport lalai (reducer);

Buat fail reducers anda di src / reducers / cart.js dan tampal kod ini:

// keadaan awal
const initState = {
  isCartBuka: palsu,
  checkout: {lineItems: []},
  produk: [],
  kedai: {}
}
// tindakan
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'
/ pengurangan
lalai eksport (negeri = initState, aksi) => {
  suis (action.type) {
    kes CLIENT_CREATED:
      kembali {... nyatakan, pelanggan: action.payload}
    kes PRODUCTS_FOUND:
      kembali {... nyatakan, produk: action.payload}
    kes CHECKOUT_FOUND:
      kembali {... nyatakan, checkout: action.payload}
    kes SHOP_FOUND:
      kembali {... negeri, kedai: action.payload}
    kes ADD_VARIANT_TO_CART:
      kembali {... nyatakan, isCartOpen: action.payload.isCartOpen, checkout: action.payload.checkout}
    kes UPDATE_QUANTITY_IN_CART:
      kembali {... nyatakan, checkout: action.payload.checkout}
    kes REMOVE_LINE_ITEM_IN_CART:
      kembali {... nyatakan, checkout: action.payload.checkout}
    kes OPEN_CART:
      kembali {... menyatakan, isCartOpen: true}
    kes CLOSE_CART:
      kembali {... menyatakan, isCartOpen: false}
    lalai:
      kembali negeri
  }
}

Jangan risau, saya tidak akan hanya menghantar pengurangan besar ini dan tidak membincangkan apa yang sedang berlaku; kita akan sampai ke setiap acara! Terdapat beberapa perkara yang perlu diperhatikan di sini.

Kami mengambil keadaan permulaan dari apa negeri ditulis seperti dalam contoh Shopify GitHub dan meletakkannya di initState kami, iaitu empat bahagian berikut negeri:

isCartBuka: palsu,
checkout: {lineItems: []},
produk: [],
kedai: {}

Walau bagaimanapun, dalam pelaksanaan saya, saya juga membuat sebahagian pelanggan negeri. Saya memanggil fungsi createClient () sekali dan kemudian segera menetapkannya dalam keadaan Redux dalam index.js. Jadi, mari kepala ke index.js:

Kembali ke index.js

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

Di Shopify membeli contoh SDK, terdapat beberapa panggilan async untuk mendapatkan maklumat mengenai produk dan menyimpan maklumat dalam fungsi componentWillMount () React. Kod contoh itu kelihatan seperti ini:

componentWillMount () {
    this.props.client.checkout.create (). kemudian ((res) => {
      this.setState ({
        checkout: res,
      });
    });
this.props.client.product.fetchAll (). kemudian ((res) => {
      this.setState ({
        produk: res,
      });
    });
this.props.client.shop.fetchInfo (). then ((res) => {
      this.setState ({
        kedai: res,
      });
    });
  }

Saya memilih untuk melakukannya sebaliknya hulu beban tapak yang mungkin, secara langsung di index.js. Kemudian, saya mengeluarkan satu peristiwa yang sama apabila setiap bahagian respons telah diterima:

// buildClient () adalah segerak, jadi kita boleh memanggil semua ini selepas!
client.product.fetchAll (). kemudian ((res) => {
  store.dispatch ({type: 'PRODUCTS_FOUND', payload: res});
});
client.checkout.create (). kemudian ((res) => {
  store.dispatch ({type: 'CHECKOUT_FOUND', muatan: res});
});
client.shop.fetchInfo (). kemudian ((res) => {
  store.dispatch ({type: 'SHOP_FOUND', muatan: res});
});

Sekarang reducer dicipta, dan permulaan klien Shopify API lengkap untuk index.js.

Kembali ke App.js

Sekarang di App.js, kembalikan kedai Redux ke keadaan App:

import {connect} dari 'react-redux';

dan jangan lupa untuk mengimport kedai juga:

kedai import daripada '.stst';

Di bahagian bawah mana apl lalai eksport perlu, ubah suai untuk ini:

lalai eksport menyambung ((negeri) => negeri) (App);

Ini menghubungkan keadaan Redux kepada komponen App.

Sekarang dalam fungsi render () kami dapat mengakses keadaan Redux dengan getState Redux () (seperti yang diguna pakai untuk menggunakan this.state reaksi vanila):

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

Akhirnya: Pengendali Acara (Kami Masih dalam App.js)

Dari atas, anda tahu bahawa terdapat hanya tiga pengendali acara yang kami perlukan di App.js, kerana kereta hanya menggunakan tiga: updateQuantityInCart, removeLineItemInCart, dan handleCartClose. Pengendali acara kereta asal dari contoh repositori GitHub, yang menggunakan keadaan komponen tempatan seperti ini:

updateQuantityInCart (lineItemId, quantity) {
  const checkoutId = this.state.checkout.id
  const lineItemsToUpdate = [{id: lineItemId, kuantiti: parseInt (kuantiti, 10)}]
kembali this.props.client.checkout.updateLineItems (checkoutId, lineItemsToUpdate) .then (res => {
    this.setState ({
      checkout: res,
    });
  });
}
removeLineItemInCart (lineItemId) {
  const checkoutId = this.state.checkout.id
kembali this.props.client.checkout.removeLineItems (checkoutId, [lineItemId]). kemudian (res => {
    this.setState ({
      checkout: res,
    });
  });
}
handleCartClose () {
  this.setState ({
    isCartBuka: palsu,
  });
}

Kita boleh refactor mereka untuk menghantar peristiwa ke kedai Redux seperti berikut:

updateQuantityInCart (lineItemId, quantity) {
    state const = store.getState (); // negeri dari kedai redux
    const checkoutId = state.checkout.id
    const lineItemsToUpdate = [{id: lineItemId, kuantiti: parseInt (kuantiti, 10)}]
    state.client.checkout.updateLineItems (checkoutId, lineItemsToUpdate) .then (res => {
      store.dispatch ({type: 'UPDATE_QUANTITY_IN_CART', muatan: {checkout: res}});
    });
}
removeLineItemInCart (lineItemId) {
    state const = store.getState (); // negeri dari kedai redux
    const checkoutId = state.checkout.id
    state.client.checkout.removeLineItems (checkoutId, [lineItemId]). kemudian (res => {
      store.dispatch ({type: 'REMOVE_LINE_ITEM_IN_CART', muatan: {checkout: res}});
    });
}
handleCartClose () {
    store.dispatch ({type: 'CLOSE_CART'});
}
handleCartOpen () {
    store.dispatch ({type: 'OPEN_CART'});
}

Sekiranya anda mengikuti, saya sudah menyebutkan bahawa saya telah menambah fungsi handleCartOpen saya sendiri, kerana saya lulus fungsi itu sebagai prop ke komponen