Un tutoriel détaillé: comment utiliser l’API Shopify de la boutique en ligne avec React et Redux

Le commerce électronique pour tous! (… Sites, c'est-à-dire )

Rédigé par Chris Août 2018, mis à jour en novembre 2018

Courtoisie de Negative Space sur pexels.com

Contexte et motivation

Donc, la motivation ici était assez simple. Je souhaitais que les visiteurs de mon site puissent parcourir, rechercher et sélectionner des produits directement sur mon domaine personnalisé sans devoir accéder à notre site Shopify.

La motivation secondaire est que je préférerais avoir ma propre base de code pour un site Web plutôt que l’un des modèles d’usine de Shopify. Aucune infraction Shopify équipe! Les modèles sont modernes et propres, mais ils sont plutôt basiques. Je suis sûr que ces modèles sont hautement personnalisables, mais ce n’est pas une pile que je connaisse pour le moment.

Il s’agit donc du meilleur des deux mondes: mon site React personnalisé (déjà construit et en ligne), avec l’API ajoutée et le processus de paiement de Shopify!

À la fin de ce didacticiel, vous pourrez ajouter vos produits Shopify sur n’importe quelle page de votre site. Le seul élément du processus d’achat qui se produira sur Shopify est lorsque l’utilisateur clique sur "Paiement".

J'ai également créé un référentiel standard vide pour ce didacticiel.

La motivation spécifique pour écrire ici sur Medium était simplement que je ne pouvais pas trouver de tutoriel sur ce processus - alors j’ai décidé de le créer!

Je suis développeur professionnel depuis 4 ans maintenant et programmant depuis 7 ans. J'ai travaillé dans des piles de technologies de la vieille école Fortran et Perl, pour React, Javascript, Python et Node.

Siren Apparel est l’une de mes sociétés de projet / start-up / fabricant que je dirige depuis 5 ans maintenant et nous avons déjà fait des dons à 5 services de police et de pompiers différents!

Commençons enfin avec ce tutoriel.

API Shopify de la boutique en ligne

Les gens formidables de Shopify ont mis en place l'API Storefront. Avec l’API Storefront, vous pouvez créer des composants React pour ajouter des images de produit, des variantes de produit, des tailles de produit, un panier et des boutons "Ajouter au panier" et "Passer à la caisse" dans votre propre site, non Shopify.

* Notez que ce tutoriel ne concerne PAS Shopify Polaris, qui est utilisé pour créer des composants dans la gestion du magasin React for Shopify.

Mise en route: Référentiel react-js-buy

Jetez un coup d'œil à cet exemple de React construit par l'équipe Shopify. La plupart du code de ce tutoriel provient de ce référentiel.

… Vous avez jeté un coup d'oeil? Bien!

Nous allons maintenant passer directement au code! Allez dans le dossier racine de votre site React et installez le module shopify-buy via le terminal:

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

(ou un fil ajouter shopify-acheter si vous préférez le fil)

Ensuite, dans votre fichier index.js frontend (PAS App.js!), Vous devrez importer le client à partir du SDK de JS Buy:

client d'importation de 'shopify-buy';

Ajoutez ensuite l'objet de configuration suivant au-dessus de l'appel ReactDOM.render ():

const client = Client.buildClient ({
    storefrontAccessToken: 'votre-jeton d'accès',
    domaine: 'your-shopify-url.myshopify.com'
});

C’est tout pour index.js pour le moment - nous y reviendrons bientôt.

Nous allons maintenant ajouter tous les composants nécessaires à une expérience de magasinage et de paiement en douceur. Copiez tous les composants du référentiel react-js-buy:

Cart.js

LineItem.js

Produit.js

Produits.js

VariantSelector.js

Nous allons coller ces composants dans le dossier acomponents / shopify / de votre dossier src /. Vous pouvez placer ces fichiers de composants n'importe où ailleurs dans le dossier src /, si vous le souhaitez. Le reste du didacticiel suppose que vous les avez mis dans components / shopify /.

Modifier App.js

App.js nécessitera d'importants changements. Commencez par importer le composant Cart que vous venez de copier dans votre propre projet:

importer un panier à partir de './components/shopify/Cart';

Si votre composant App.js était sans état, comme le mien, vous devez copier en toute sécurité l'ensemble de la fonction constructeur ():

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

Si vous avez déjà l'état, copiez uniquement ces lignes de reliure. Ces trois lignes sont des fonctions de gestionnaire d'événements dont le panier Shopify a besoin pour fonctionner correctement.

"Mais qu'en est-il de l'état pour le panier!?"

Tu peux demander; ou:

"Qu'en est-il de définir ces gestionnaires d'événements pour le panier!?"

En effet, cela arrive, mais pas encore!

Vous pouvez ensuite ajouter le composant au bas de votre fonction render (), avant le div final.

À mon avis, le panier devrait être accessible n'importe où dans votre application. Je pense qu'il est donc logique de placer le composant dans le composant racine de votre application - en d'autres termes, App.js:

revenir (
... )

Encore une fois, je n’ai pas encore ajouté de code sur les gestionnaires d’événements pour le panier. De plus, je n'ai pas abordé le manque de composants d'état pour le panier dans App.js.

Il y a de bonnes raisons pour cela.

À peu près à mi-parcours de ce projet, j'ai réalisé que la composante produits ne se trouvait bien sûr pas dans mon fichier App.js.

Au lieu de cela, il a été enterré environ trois composants enfants.

Ainsi, au lieu de transmettre les produits sur trois niveaux aux enfants, puis aux gestionnaires de fonctions jusqu’à remonter…

J'ai décidé d'utiliser…

Redux !!!

Pouah! Je sais, je sais, Redux, sans être très difficile, est une douleur dans le% * $! câbler initialement avec tout le passe-partout requis. Toutefois, si vous êtes un développeur travaillant sur un magasin de commerce électronique ou un propriétaire de magasin de commerce électronique, réfléchissez de cette façon: Redux vous permettra d'accéder à l'état du panier à partir de n'importe quel composant ou page de notre site Web ou de notre application Web.

Cette capacité sera essentielle à mesure que Siren Apparel s'agrandit et que nous développons davantage de produits. Au fur et à mesure que nous créerons plus de produits, je créerai une page de magasin dédiée séparée avec tous les produits, tout en ne laissant qu'une poignée de produits en vedette sur la page d'accueil.

La possibilité d'accéder au panier est essentielle si un utilisateur magasine un peu, lit des histoires ou des informations sur Siren Apparel, puis décide de passer à la caisse. Peu importe combien ils naviguent, rien dans leur panier ne sera perdu!

En bref, j’ai décidé qu’il serait probablement préférable d’implémenter Redux maintenant, alors que la base de code de notre site n’est pas trop volumineuse.

Implémentation de Redux pour Shopify Buy SDK avec Boilerplate Bare Minimum

Installez les paquets NPM redux et react-redux:

npm installer --save redux réagir-redux

Dans index.js, importez Provider de react-redux et votre magasin à partir de ./store:

importer {fournisseur} de 'react-redux';
importer magasin de './store';

Enroulez le composant avec le magasin transféré autour de votre dans index.jsto pour connecter votre application à votre magasin Redux:

ReactDOM.render (

    
      
    
 ,
document.getElementById ('root')
)

(Notez que j’ai aussi un , mais cela se trouve dans un article différent sur la façon dont j’ai appliqué l’internationalisation et la localisation pour rendre le contenu de manière dynamique sur le site de Siren Apparel. Une histoire différente pour un jour différent.)

Bien sûr, nous n’avons pas encore créé de fichier ./store.js. Créez votre magasin dans store.jsin le src / root et mettez-le dedans:

importer {createStore} à partir de 'redux';
importateur réducteur de './reducers/cart';
export par défaut createStore (réducteur);

Créez votre fichier réducteurs dans src / reducers / cart.js et collez ce code:

// Etat initial
const initState = {
  isCartOpen: false,
  checkout: {lineItems: []},
  des produits: [],
  magasin: {}
}
// Actions
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'
// réducteurs
export default (state = initState, action) => {
  commutateur (action.type) {
    case CLIENT_CREATED:
      return {... state, client: action.payload}
    case PRODUITS_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}
    défaut:
      état de retour
  }
}

Ne vous inquiétez pas, je ne vais pas simplement poster ce gros réducteur et ne pas parler de ce qui se passe; nous aurons à chaque événement! Il y a quelques choses à noter ici.

Nous prenons l'état initial à partir duquel l'état est écrit comme dans l'exemple Shopify GitHub et le mettons dans notre initState, à savoir les quatre parties d'état suivantes:

isCartOpen: false,
checkout: {lineItems: []},
des produits: [],
magasin: {}

Cependant, dans mon implémentation, je crée également une partie cliente de l'état. J'appelle la fonction createClient () une fois, puis la mets immédiatement à l'état Redux dans index.js. Alors passons à index.js:

Retour à index.js

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

Dans l'exemple du kit de développement logiciel d'achat Shopify, il existe quelques appels asynchrones pour obtenir des informations sur les produits et stocker des informations dans la fonction ComponentWillMount () de React. Cet exemple de code ressemble à ceci:

composantWillMount () {
    this.props.client.checkout.create (). then ((res) => {
      this.setState ({
        caisse: res,
      });
    });
this.props.client.product.fetchAll (). then ((res) => {
      this.setState ({
        produits: res,
      });
    });
this.props.client.shop.fetchInfo (). then ((res) => {
      this.setState ({
        boutique: res,
      });
    });
  }

J'ai choisi de le faire aussi loin que possible en amont d'une charge de site, directement dans index.js. Ensuite, j'ai émis un événement correspondant lorsque chaque partie de la réponse a été reçue:

// buildClient () est synchrone, nous pouvons donc les appeler après!
client.product.fetchAll (). then ((res) => {
  store.dispatch ({type: 'PRODUCTS_FOUND', charge utile: res});
});
client.checkout.create (). then ((res) => {
  store.dispatch ({type: 'CHECKOUT_FOUND', charge utile: res});
});
client.shop.fetchInfo (). then ((res) => {
  store.dispatch ({type: 'SHOP_FOUND', charge utile: res});
});

A présent, le réducteur est créé et l'initialisation du client API Shopify est terminée pour index.js.

Retour à App.js

Maintenant dans App.js, connectez le magasin de Redux à l’état de l’application:

importer {connect} de 'react-redux';

et n'oubliez pas d'importer le magasin également:

importer magasin de './store';

Au bas de la liste des applications d'exportation par défaut, modifiez-le comme suit:

connexion par défaut à l'exportation ((état) => état) (App);

Cela connecte l'état Redux au composant App.

Désormais, dans la fonction render (), nous pouvons accéder à l’état de Redux avec getState () (ce qui revient à utiliser this.state de la réaction de vanilla):

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

Enfin: les gestionnaires d’événements (nous sommes toujours dans App.js)

À partir d’en haut, vous savez qu’il n’ya que trois gestionnaires d’événements dont nous avons besoin dans App.js, car le panier n’en utilise que trois: updateQuantityInCart, removeLineItemInCart et handleCartClose. Les gestionnaires d'événements de panier d'origine de l'exemple de référentiel GitHub, qui utilisait l'état du composant local, ressemblaient à ceci:

updateQuantityInCart (lineItemId, quantité) {
  const checkoutId = this.state.checkout.id
  const lineItemsToUpdate = [{id: lineItemId, quantité: parseInt (quantité, 10)}]
renvoyer this.props.client.checkout.updateLineItems (checkoutId, lineItemsToUpdate) .then (res => {
    this.setState ({
      caisse: res,
    });
  });
}
removeLineItemInCart (lineItemId) {
  const checkoutId = this.state.checkout.id
renvoyer this.props.client.checkout.removeLineItems (checkoutId, [lineItemId]). then (res => {
    this.setState ({
      caisse: res,
    });
  });
}
handleCartClose () {
  this.setState ({
    isCartOpen: false,
  });
}

Nous pouvons les restructurer pour envoyer des événements au magasin Redux comme suit:

updateQuantityInCart (lineItemId, quantité) {
    const state = store.getState (); // état de redux store
    const checkoutId = state.checkout.id
    const lineItemsToUpdate = [{id: lineItemId, quantité: parseInt (quantité, 10)}]
    state.client.checkout.updateLineItems (checkoutId, lineItemsToUpdate) .then (res => {
      store.dispatch ({type: 'UPDATE_QUANTITY_IN_CART', charge utile: {checkout: res}});
    });
}
removeLineItemInCart (lineItemId) {
    const state = store.getState (); // état de redux store
    const checkoutId = state.checkout.id
    state.client.checkout.removeLineItems (checkoutId, [lineItemId]). then (res => {
      store.dispatch ({type: 'REMOVE_LINE_ITEM_IN_CART', charge utile: {checkout: res}});
    });
}
handleCartClose () {
    store.dispatch ({type: 'CLOSE_CART'});
}
handleCartOpen () {
    store.dispatch ({type: 'OPEN_CART'});
}

Si vous suiviez, j'ai déjà mentionné que j'avais ajouté ma propre fonction handleCartOpen, car je transmettais cette fonction en tant qu'accessoire à mon composant