shopify二次开发教程_详细教程:如何将Shopify的Storefront API与React和Redux结合使用...

shopify二次开发教程

by Chris Frewin

克里斯·弗里温(Chris Frewin)

详细教程:如何将Shopify的Storefront API与React和Redux结合使用 (A detailed tutorial: how to use Shopify’s Storefront API with React and Redux)

电子商务为所有人! (…网站,就是?) (E-commerce for all! (…websites, that is ?))

Written by Chris August 2018, updated November, 2018

克里斯(Chris) 2018年8月撰写,2018年11月更新

背景与动机 (Background and Motivation)

So the motivation here was pretty simple. I wanted my site visitors to be able to browse, search, and select products directly on my custom domain without having to go to our Shopify site.

因此,这里的动机非常简单。 我希望网站访问者能够直接在我的自定义域上浏览,搜索和选择产品,而不必访问我们的Shopify网站。

The secondary motivation is that I’d much rather have my own codebase for a website than use one of Shopify’s factory templates. No offense Shopify team! The templates are modern and clean, but they are rather basic. I’m sure those templates are heavily customizable, but it’s not a stack I know at the moment.

次要动机是,我宁愿拥有自己的网站代码库,也不愿使用Shopify的工厂模板之一。 Shopify团队无罪! 模板是现代且干净的,但是它们是非常基本的。 我敢肯定那些模板是高度可定制的,但是目前我还不知道它是一个堆栈。

So this is the best of both worlds — my custom React site (already built and online ?), with the added API and checkout process of Shopify!

因此,这是两全其美的方式-我的自定义React网站(已建立并在线运行?),并添加了Shopify的API和结帐流程!

By the end of this tutorial, you’ll be able to add your Shopify products on any page of your site. The only part of the shopping process that will occur on Shopify is when the user clicks ‘Checkout’.

在本教程结束时,您将可以在网站的任何页面上添加Shopify产品。 Shopify上购物过程的唯一部分是用户单击“结帐”。

I’ve created an empty boilerplate repository for this tutorial as well.

我也为本教程创建了一个空的样板存储库 。

The motivation specifically for writing here on Medium was simply that I couldn’t find a tutorial on this process myself — so I decided to make one!

专门在Medium上写代码的动机仅仅是因为我自己找不到关于此过程的教程-因此,我决定编写一个!

I’ve been a professional developer for 4 years now, and programming for 7. I’ve worked in tech stacks from old-school Fortran and Perl, to React, Javascript, Python, and Node.

我已有4年的专业开发人员经验,并且有7年的编程经验。我从事过从旧式的Fortran和Perl到React,Javascript,Python和Node的技术堆栈。

Siren Apparel is one of my side project / startup / maker companies that I’ve run for 5 years now, and we’ve donated to 5 different police and fire departments so far!

Siren Apparel是我运营了5年的副项目/启动/制造商公司之一,到目前为止,我们已向5个不同的警察和消防部门捐款!

Let’s finally get started with this tutorial.

最后,让我们开始学习本教程。

Shopify的店面API (Shopify’s Storefront API)

The wonderful folks at Shopify have put together the Storefront API. With the Storefront API, you can create React components to add product pictures, product variations, product sizes, a cart, and ‘add to cart’ and ‘checkout’ buttons into your own, non-Shopify site.

Shopify的出色人士汇集了Storefront API 。 使用Storefront API,您可以创建React组件以将产品图片,产品变体,产品尺寸,购物车以及“添加到购物车”和“结帐”按钮添加到您自己的非Shopify网站中。

*Note that this tutorial is NOT about Shopify Polaris, which is used to create components in React for Shopify store management itself.

*请注意,本教程与Shopify Polaris无关 ,后者用于在React for Shopify商店管理本身中创建组件。

入门: react-js-buy存储库 (Getting Started: react-js-buy Repository)

Take a look at this React example built by the Shopify team. Most of the code in this tutorial comes from that repository.

看一下Shopify团队构建的这个React示例 。 本教程中的大多数代码都来自该存储库。

…Did you take a look? Good! ?

…你看了吗? 好! ?

Now we’re going to hop right into code! Head to your React site’s root folder and install the shopify-buy module via the terminal:

现在,我们将直接跳入代码! 转到您的React站点的根文件夹,并通过终端安装shopify-buy模块:

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

(or yarn add shopify-buy if you prefer yarn)

(或者如果您更喜欢yarn yarn add shopify-buy )

Then, in your frontend index.js, (NOT App.js!) you will need to import Client from the JS Buy SDK:

然后,在前端index.js (不是App.js !)中,您需要从JS Buy SDK导入Client

import Client from 'shopify-buy';

Then add the following configuration object above the ReactDOM.render()call:

然后在ReactDOM.render()调用上方添加以下配置对象:

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

That’s it for index.js for now — we’ll come back to it soon.

目前,这就是index.js的内容-我们将尽快恢复。

Now we’re going to add in all the components needed for a smooth shopping and checkout experience. Copy all the components from the react-js-buy repository:

现在,我们将添加所需的所有组件,以实现顺畅的购物和结帐体验。 从react-js-buy存储库复制所有组件:

Cart.js

Cart.js

LineItem.js

LineItem.js

Product.js

Product.js

Products.js

Products.js

VariantSelector.js

VariantSelector.js

We will paste these components into acomponents/shopify/ folder in your src/ folder. You could put these component files anywhere else in the src/ folder, if you wished. The rest of the tutorial assumes you have put them in components/shopify/ .

我们会将这些组件粘贴到src/文件夹中的components/shopify/文件夹中。 如果需要,可以将这些组件文件放在src/文件夹中的其他任何位置。 本教程的其余部分假定您已将它们放在components/shopify/

修改App.js (Modifying App.js)

App.js will need extensive changes. First, import that Cart component you just copied into your own project:

App.js将需要进行大量更改。 首先,将刚复制的Cart组件导入到自己的项目中:

import Cart from './components/shopify/Cart';

If your App.js component was stateless, like mine, you should be safe copying this entire constructor() function:

如果您的App.js组件像我的一样是无状态的,则应该安全地复制以下整个constructor()函数:

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

If you already have state, copy only those bind lines. Those three lines are event handler functions that the Shopify cart needs to function properly.

如果您已经有状态,则仅复制那些bind线。 这三行是Shopify购物车正常运行所需的事件处理函数。

“But what about state for the cart!?”
“但是购物车的状态呢??”

You may ask; or:

您可能会问; 要么:

“What about defining those event handlers for the cart!?”
“如何为购物车定义那些事件处理程序呢??”

Indeed, that’s coming, but not yet! ?

确实,那是即将到来的,但还没有! ?

You can then append the <Cart/> component to the bottom of your render() function, before the ending div.

然后,您可以将<Car t />组件附加到your re render()函数的底部,在结束div之前。

In my opinion, the cart should be accessible anywhere in your app. I think it makes sense, then, to put the <Cart/> component in the root component of your app — in other words, App.js:

我认为,购物车应该可以在您应用的任何位置访问。 因此,我认为将<Car t />组件放入应用程序的根组件中是有意义的—在其他代码中ords, App.js:

return (<div>...<Cart    checkout={this.state.checkout}    isCartOpen={this.state.isCartOpen}    handleCartClose={this.handleCartClose}    updateQuantityInCart={this.updateQuantityInCart}    removeLineItemInCart={this.removeLineItemInCart} /></div>);

Again, I haven’t included any code on the event handlers for the cart yet. Additionally, I didn’t address the lack of state components for the cart in App.js.

同样,我还没有在购物车的事件处理程序中包含任何代码。 此外,我没有解决App.js中购物车缺少状态组件的问题。

There is good reason for this.

这有充分的理由。

About halfway through this project, I realized my products component was of course not in my App.js file.

在这个项目进行到一半的时候,我意识到我的产品组件当然不在我的App.js文件中。

Instead, it was buried about three children components down.

相反,它被埋在大约三个子组件中。

So instead of passing products three levels down to children, and then function handlers all the way back up…

因此,与其将产品的三个层次传递给子级,然后再将函数处理程序一路备份……

I decided to use…

我决定使用...

? Redux!!! ?

Redux !!!

Ugh! I know, I know, Redux, while not being very difficult, is a pain in the %*$! to wire up initially with all the boilerplate required. But, if you are a developer working on an E-commerce store or an E-commerce store owner, think of it this way: Redux will enable you to access the state of the cart from any component or page in our website or webapp.

啊! 我知道,虽然Redux并不困难,但它对%* $来说是一个痛苦! 首先连接所需的所有样板。 但是,如果您是在电子商务商店中工作的开发人员或电子商务商店所有者,请这样考虑:Redux将使您能够从我们网站或webapp中的任何组件或页面访问购物车的状态。

This ability will be essential as Siren Apparel expands and we develop more products. As we create more products, I’ll make a separate dedicated store page with all products, while leaving just a handful of featured products on the homepage.

随着Siren服装的扩展以及我们开发更多产品,这一能力将至关重要。 随着我们创建更多产品,我将在所有产品上创建一个单独的专用商店页面,同时在首页上仅保留少数特色产品。

The ability to access the cart is essential if a user shops around a bit, reads some stories or info about Siren Apparel, and then decides to checkout. It doesn’t matter how much they navigate around, nothing from their cart will be lost!

如果用户四处逛逛,阅读一些有关Siren Apparel的故事或信息, 然后决定结帐, 那么访问购物车的能力至关重要。 不管导航多少,他们的购物车都不会丢失!

So, in short, I decided it’s probably better to implement Redux now while the codebase for our site isn’t too large.

因此,简而言之,我认为在我们站点的代码库不太大的情况下,现在最好实现Redux。

使用裸机最小模板为Shopify Buy SDK实施Redux (Implementing Redux for Shopify Buy SDK With Bare Minimum Boilerplate)

Install NPM packages redux and react-redux:

安装NPM软件包reduxreact-redux

npm install --save redux react-redux

npm install --save redux react-redux

In index.js , import Provider from react-redux and your store from ./store:

index.js ,从react-redux导入Provider ,并从./store导入您的store

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

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

Wrap the <Provider> component with the passed store around your&lt;App>;in index.jsto hook up your App to your Redux store:

包裹<Provid ER>组件与p assed商店不要蜘蛛d you [R&L t;App> ;在index.jsto勾你的应用程序到您的终极版店:

ReactDOM.render(<Provider store={store}>    <IntlProvider locale={locale} messages={flattenMessages(messages[locale.substring(0, 2)])}>      <App locale={locale}/>    </IntlProvider> </Provider>,document.getElementById('root'));

ReactDOM.render( <Provider store={store}> <IntlProvider locale={locale} messages={flattenMessages(messages[locale.substring(0, 2)])}> <App locale={locale}/> </IntlProvider> </Provider>, document.getElementById('root') );

(Note that I also have a <IntlProvider>, but that’s in a different post about how I applied internationalization and localization to dynamically render the content on Siren Apparel’s site. A different story for a different day.)

(请注意,我也有一个<IntlProvid er>,但这是在另一篇有关如何应用国际化和本地化来动态呈现Siren Apparel网站上内容的文章中 。关于另一天的不同故事。)

Now of course we haven’t made a ./store.js file yet. Create your store in store.jsin the src/ root and put this in it:

当然,现在我们还没有制作./store.js文件。 src/根目录下的store.js中创建商店, store.js其放入其中:

import {createStore} from 'redux';import reducer from './reducers/cart';export default createStore(reducer);

import {createStore} from 'redux'; import reducer from './reducers/cart';export default createStore(reducer);

Create your reducers file in src/reducers/cart.js and paste this code:

src/reducers/cart.js创建您的reducers文件,然后粘贴以下代码:

// initial stateconst initState = {  isCartOpen: false,  checkout: { lineItems: [] },  products: [],  shop: {}}// actionsconst 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'// reducersexport 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}    default:      return state  }}

// initial state const initState = { isCartOpen: false, checkout: { lineItems: [] }, products: [], shop: {} }// 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'// reducers 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} 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} default: return state } }

Don’t worry, I’m not going to just post this big reducer and not discuss what is going on; we’ll get to each event! There are a few things to note here.

不用担心,我不会只发布这个大型的reducer,而不会讨论正在发生的事情。 我们将参加每个活动! 这里有几件事要注意。

We take the initial state from what the state is written as in the Shopify GitHub example and put it in our initState, namely the following four parts of state:

我们从Shopify GitHub示例中编写的状态中获取初始状态,并将其放入我们的initState ,即状态的以下四个部分:

isCartOpen: false,checkout: { lineItems: [] },products: [],shop: {}

isCartOpen: false, checkout: { lineItems: [] }, products: [], shop: {}

However, in my implementation, I also create a client part of the state. I call the createClient() function once and then immediately set it in the Redux state in index.js . So let’s head into index.js:

但是,在实现中,我还创建了状态的client部分。 我调用一次createClient()函数,然后立即在index.js中将其设置为Redux状态。 因此,让我们进入index.js

返回index.js (Back to index.js)

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

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

In the Shopify buy SDK example, there are a few async calls to get information about the products and store information in React’s componentWillMount() function. That example code looks like this:

在Shopify购买SDK示例中,有一些异步调用来获取有关产品的信息并将信息存储在React的componentWillMount()函数中。 该示例代码如下所示:

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

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

I opted to do that instead as far upstream of a site load as possible, directly in index.js. Then, I issued a corresponding event when each part of the response has been received:

我选择直接在index.js这样做,而不是在站点负载的上游进行。 然后,在收到响应的每个部分时,我发出了一个相应的事件:

// buildClient() is synchronous, so we can call all these after!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});});

// buildClient() is synchronous, so we can call all these after! 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}); });

By now the reducer is created, and the initialization of the Shopify API client is complete all for index.js.

至此,已经创建了reducer,并且已经针对index.js完成了Shopify API client的初始化。

回到App.js (Back to App.js)

Now in App.js, wire up Redux’s store to the App state:

现在在App.js ,将Redux的商店连接到App状态:

import { connect } from 'react-redux';

import { connect } from 'react-redux';

and don’t forget to import the store as well:

并且不要忘记导入商店:

import store from './store';

import store from './store';

At the bottom where export default App should be, modify it to this:

在应该export default App的底部, export default App其修改为:

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

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

This connects the Redux state to the App component.

这会将Redux状态连接到App组件。

Now in the render() function we are able to access the Redux’s state with Redux’s getState() (as apposed to using vanilla react’s this.state):

现在,在render()函数中,我们可以使用Redux的getState()访问Redux的getState()与使用vanilla react的this.state ):

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

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

最后:事件处理程序(我们仍在App.js中) (Finally: the Event Handlers (We’re Still in App.js))

From above, you know that there are only three event handlers that we need in App.js, because the cart uses only three: updateQuantityInCart, removeLineItemInCart, and handleCartClose. The original cart event handlers from the example GitHub repository, which used local component state looked like this:

从上面知道,在App.js中我们只需要三个事件处理程序,因为购物车仅使用三个事件处理程序: updateQuantityInCartremoveLineItemInCarthandleCartClose 示例GitHub存储库中使用本地组件状态的原始购物车事件处理程序如下所示:

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

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

We can refactor them to dispatch events to the Redux store as follows:

我们可以重构它们以将事件调度到Redux存储,如下所示:

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'});}

updateQuantityInCart(lineItemId, quantity) { const state = store.getState(); // state from redux store const state = store.getState(); // state from redux store const checkoutId = state.checkout.id 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 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'}); }

If you were following along, I already mentioned that I added my own handleCartOpen function, because I pass that function down as a prop to my <Nav/> component, so a user is able to open and close the cart from a link in the nav. At a future time, I could move that function to the Nav itself instead of passing it as a prop, since of course the Redux store will also be available there!

如果您一直在学习,我已经提到我添加了自己的handleCartOpen函数,因为我将该函数作为道具传递给了<Na v />组件,因此用户可以从中的链接打开和关闭购物车。导航。 将来,我可以将该功能移至Nav本身,而不是将其作为道具传递,因为当然Redux商店也将在那里可用!

最后添加<Products />组件! (Finally Add that <Products/> Component!)

So, you’ve got a basic store maybe with some simple href’s that link to the corresponding product on your Shopify store? Ha! Get rid of those, and replace them with your brand spankin’ new <Products/> component!

因此,您有一家基本商店,可能带有一些简单的href ,它们链接到Shopify商店中的相应产品? 哈! 摆脱这些,并用您的品牌spankin'新的<Product s />组件替换它们!

First, import the component into wherever your store markup should be (remember, in my code base I’ve put the shopify example components in a folder called shopify/)

首先,将组件导入您商店标记应在的任何位置(请记住,在我的代码库中,我已经将shopify示例组件放在了一个名为shopify/的文件夹中)

This will be wherever your products currently are. (In the boilerplate repository I made, I put this in the GenericProductsPage component, to signal that this code could be applied to any page that has a products section):

这将是您当前产品的任何位置。 (在我制作的样板存储库中 ,将其放入GenericProductsPage组件中,以表明该代码可以应用于具有products部分的任何页面):

import Products from './shopify/Products';

import Products from './shopify/Products';

Now finally, that past 15–20 minutes of redux boilerplate code edits pays off: we can grab the products part of our state — not by way of vanilla React state passed down over and over again through props — but through grabbing by way of Redux state, in a neat one liner const state = store.getState();:

现在终于可以了,过去15到20分钟的redux样板代码编辑取得了回报:我们可以获取状态的products部分-而不是通过props一遍又一遍传递的香草React状态-而是通过Redux进行获取状态,在一个整洁的线性const state = store.getState();

render () {    const state = store.getState(); // state from redux store    let oProducts = <Products      products={state.products}      client={state.client}      addVariantToCart={this.addVariantToCart}    />;

render () { const state = store.getState(); // state from redux store const state = store.getState(); // state from redux store let oProducts = <Products products={state.products} client={state.client} addVariantToCart={this.addVariantToCart} />;

Don’t forget to drop the component itself into where it should go in your render() function. For me, that location was buried in Bootstrap style classes and HTML:

不要忘记将组件本身放到render()函数中应该放置的位置。 对我来说,该位置埋在Bootstrap样式类和HTML中:

...<div className="service-content-one">    <div className="row">        <Products/>    </div>{/*/.row*/}</div>{/*/.service-content-one*/}...

... <div className="service-content-one"> <div className="row"> <Products/> </div>{/*/.row*/} </div>{/*/.service-content-one*/} ...

Finally, we will need a single event function addVariantToCart for the cart to work with this products component. Again, for reference, here is the original, vanilla React local state version of addVariantToCart(again, from the shopify example repository):

最后,我们需要一个事件函数addVariantToCart ,购物车才能与此产品组件一起使用。 再次,作为参考,这里是addVariantToCart的原始香草React本地state版本(同样,来自shopify示例存储库):

addVariantToCart(variantId, quantity){  this.setState({    isCartOpen: true,  });const lineItemsToAdd = [{variantId, quantity: parseInt(quantity, 10)}]  const checkoutId = this.state.checkout.idreturn this.props.client.checkout.addLineItems(checkoutId, lineItemsToAdd).then(res => {    this.setState({      checkout: res,    });  });}

addVariantToCart(variantId, quantity){ this.setState({ isCartOpen: true, });const lineItemsToAdd = [{variantId, quantity: parseInt(quantity, 10)}] const checkoutId = this.state.checkout.idreturn this.props.client.checkout.addLineItems(checkoutId, lineItemsToAdd).then(res => { this.setState({ checkout: res, }); }); }

and the new, Redux-friendly store.dispatch() version:

以及对Redux友好的新store.dispatch()版本:

addVariantToCart(variantId, quantity) {    const state = store.getState(); // state from redux store    const lineItemsToAdd = [{variantId, quantity: parseInt(quantity, 10)}]    const checkoutId = state.checkout.id    state.client.checkout.addLineItems(checkoutId, lineItemsToAdd).then(res => {      store.dispatch({type: 'ADD_VARIANT_TO_CART', payload: {isCartOpen: true, checkout: res}});    });}

addVariantToCart(variantId, quantity) { const state = store.getState(); // state from redux store const state = store.getState(); // state from redux store const lineItemsToAdd = [{variantId, quantity: parseInt(quantity, 10)}] const state = store.getState(); // state from redux store const lineItemsToAdd = [{variantId, quantity: parseInt(quantity, 10)}] const checkoutId = state.checkout.id state.client.checkout.addLineItems(checkoutId, lineItemsToAdd).then(res => { store.dispatch({type: 'ADD_VARIANT_TO_CART', payload: {isCartOpen: true, checkout: res}}); }); }

which is of course the one we will use. ?

当然,这是我们将要使用的那个。

Don’t forget to bind it in the constructor:

不要忘记将其绑定到构造函数中:

this.addVariantToCart = this.addVariantToCart.bind(this);

this.addVariantToCart = this.addVariantToCart.bind(this);

Also, you’ll need to connect this component to the store like you did App.js , and import the store:

另外,您需要像将App.js一样将此组件连接到商店,并导入商店:

import { connect } from 'react-redux'import store from '../store';

import { connect } from 'react-redux' import store from '../store'; import { connect } from 'react-redux' import store from '../store';

at the top, and (assuming the component where you put the Shopify Product component name is GenericProductPage:

在顶部,并且(假设您将Shopify Product组件放在其中的组件名称为GenericProductPage

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

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

at the bottom.

在底部。

Great! Now, no matter how deeply buried in components, or wherever your products component is declared, it can communicate with the cart’s state!

大! 现在,无论埋在组件中的深度如何,或者无论您的产品组件在哪里声明,它都可以与购物车的状态进行通信!

最终奖金示例:标题或导航中的购物车 (Final BONUS Example: Cart in Your Header or Nav)

If you want to have a ‘Cart’ button in your header / nav, add this button in your Nav component’s render function (again, an example from my current site, which has Bootstrap styles — a very simple version is in the boilerplate example:

如果您想在标题/导航栏中添加一个“购物车”按钮,请将此按钮添加到Nav组件的render函数中(同样,这是我当前站点的示例,该示例具有Bootstrap样式-一个非常简单的版本在样例中 :

<div className="App__view-cart-wrapper"><button className="App__view-cart" onClick={this.props.handleCartOpen}>    Cart    </button></div>

<div className="App__view-cart-wrapper"> <button className="App__view-cart" onClick={this.props.handleCartOpen}> Cart </button> </div>

where handleCartOpen is a new handler method you’ll have to add to App.js:

其中handleCartOpen是一个新的处理程序方法,您必须将其添加到App.js

constructor() {  super();  ...  this.handleCartOpen = this.handleCartOpen.bind(this);  ...}

constructor() { super(); ... this.handleCartOpen = this.handleCartOpen.bind(this); ... }

in the constructor. Then when you are referencing your Nav component in App.js (or wherever you place your Nav) you pass the function handler:

在构造函数中。 然后,当您在App.js中(或您放置Nav的任何地方)引用Nav组件时,您将传递函数处理程序:

<Nav handleCartOpen={this.handleCartOpen}/>

<Nav handleCartOpen={this.handleCartOpen}/>

This could also be refactored to an event in Redux, but since it was only one child down, I did it the vanilla React way.

也可以将其重构为Redux中的一个事件,但是由于只有一个孩子,我采用了香草的React方法。

样式组件 (Styling Component(s))

I relied on Shopify’s CSS file, app.css, located in the shared/ folder in the storefront-api-example repository (you can’t miss it, it’s the only file in shared/ )!

我依靠ShopifyCSS文件app.css ,该文件位于storefront-api-example存储库的shared/文件夹中(您不能错过它,它是shared/唯一的文件)!

Make sure to copy that into your styles/ folder or wherever it needs to be and include it in your index.js file. In my index.js it looks like this:

确保将其复制到您的styles/文件夹中或需要复制的任何位置,并将其包含在index.js文件中。 在我的index.js它看起来像这样:

import './styles/shopify.css';

import './styles/shopify.css';

Since I renamed the app.css which was in the Shopify example repository to shopify.css , and put it folder styles. This convention is also used in the boilerplate repository code.

由于我改名为app.css这是在Shopify例如存储库shopify.css ,并把它的文件夹styles 在样板存储库代码中也使用此约定。

From here it’s pretty easy to identify where exactly in shopify.css the default bright blue color for the buttons is defined, and so on. I’m going to save detailed CSS customization for you to handle. ?

从这里很容易确定在shopify.css中确切的shopify.css ,为按钮定义了默认的亮蓝色,依此类推。 我将保存详细CSS定制供您处理。

But who knows, maybe I’ll post on that eventually — but I find the styles from Shopify pretty good and easy enough to modify.

但是谁知道呢,也许我最终会在此发布—但是我发现Shopify中的样式非常好并且很容易修改。

外卖 (Takeaways)

In my opinion, this is a perfect (non-todo list ?) use of Redux. Redux cleanly organizes the event functions and state of the Shopify cart and makes it easy to access the cart’s state from any other component. This is much easier to maintain than passing pieces of state to children and using multiple event handlers to pass events back up to parent functions all over a React app.

我认为,这是Redux的完美用法(非待办事项列表?)。 Redux干净利落地组织了Shopify购物车的事件功能和状态,并使其易于从任何其他组件访问购物车的状态。 这比将状态传递给子级并使用多个事件处理程序将事件传递回整个React应用程序的父函数要容易得多。

As shown as an example in the tutorial, the cart’s state is accessed easily in the Nav component and the shop section of the front page. I’ll also be able to easily add it to a sort of ‘featured’ product section as well, once Siren Apparel is ready for that.

如本教程中的示例所示,可以在Nav组件和首页的shop部分中轻松访问购物车的状态。 一旦Siren Apparel做好了准备,我还可以轻松地将其添加到“特色”产品部分中。

查找代码 (Find the Code)

A boilerplate repository of this implementation can be found here. It is a near blank create-react-app app, but with all the changes of this tutorial implemented in index.js and App.js , as well as a super basic GenericStorePage and Nav components.

可在此处找到此实现的样板存储库。 这是一个几乎空白的create-react-app程序,但是本教程的所有更改都在index.jsApp.js ,以及超基本的GenericStorePageNav组件。

I built the code on the repo while re-reading and updating my own tutorial here, to make sure this tutorial makes sense!

在重新阅读和更新自己的教程时,我在仓库上构建了代码,以确保该教程有意义!

Because I am crazy ?, Siren Apparel’s website is all open-sourced. So if you want to fool around with my implementation, check out the repository!

因为我疯了?,Siren Apparel的网站全部开源。 因此,如果您想闲逛我的实现,请删除存储库!

I hope you enjoyed this tutorial! If anything isn’t clear or just plain not working, let me know! I’ll try to assist you!

希望您喜欢本教程! 如果有任何不清楚的地方,或者只是无法正常工作,请通知我! 我会尽力为您服务!

Thanks to Lisa Catalano at CSS-Snippets for the simple Nav example which I used in the boilerplate repository!

感谢CSS-Snippets的Lisa Catalano提供了我在样板存储库中使用的简单Nav示例 !

Cheers! ?

干杯!

Chris

克里斯

翻译自: https://www.freecodecamp.org/news/a-detailed-tutorial-how-to-use-shopifys-storefront-api-with-react-and-redux-37f95cbab7f/

shopify二次开发教程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/392713.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

element里面popover里面的高度_五斗柜的高度一般是多少 五斗柜放在什么位置好

五斗柜也就是一种抽屉收纳柜&#xff0c;目前在卧室或是书房等空间均是可以见到。其根据使用用途的不同&#xff0c;进而有着高度和款式&#xff0c;以及摆放位置等等的区别。因此&#xff0c;下面带来五斗柜的高度一般是多少、五斗柜放在什么位置好&#xff0c;以及五斗柜里面…

leetcode 57. 插入区间

给出一个无重叠的 &#xff0c;按照区间起始端点排序的区间列表。 在列表中插入一个新的区间&#xff0c;你需要确保列表中的区间仍然有序且不重叠&#xff08;如果有必要的话&#xff0c;可以合并区间&#xff09;。 示例 1&#xff1a; 输入&#xff1a;intervals [[1,3]…

《C++标准程序库》学习笔记1--第二章第三章

————————— 第二章 —————————1.&#xff08;P11&#xff09; C规定&#xff1a;除了以typename修饰外&#xff0c;template内的任何标志符号都被视为一个值(value)而非一个型别。 eg. template <classT>classMyClass{ typename T::SubType *ptr; };…

让物联网真正起飞的关键:无线充电

从一般郊区家庭到工厂装配生产线&#xff0c;我们生活中的每个角落都正在经历“智能”技术强化的过程。物联网&#xff08;IoT&#xff09;技术看似无所不在&#xff0c;但是为这些装置持续供电仍是一大挑战&#xff0c;除非这个问题能够解决&#xff0c;否则许多令人兴奋的物联…

【NOIP2016】愤怒的小鸟

题目描述 Kiana最近沉迷于一款神奇的游戏无法自拔。 简单来说&#xff0c;这款游戏是在一个平面上进行的。 有一架弹弓位于(0,0)处&#xff0c;每次Kiana可以用它向第一象限发射一只红色的小鸟&#xff0c;小鸟们的飞行轨迹均为形如的曲线&#xff0c;其中a,b是Kiana指定的参数…

leetcode 127. 单词接龙(bfs)

给定两个单词&#xff08;beginWord 和 endWord&#xff09;和一个字典&#xff0c;找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则&#xff1a; 每次转换只能改变一个字母。 转换过程中的中间单词必须是字典中的单词。 说明: 如果不存在这样的转换序…

java swing 动态生成表格_6 个曾经牛逼哄哄的 Java 技术,你用过吗?

大家好啊&#xff0c;今天给大家分享下我的开发历程中&#xff0c;我知道的那些被淘汰的技术或者框架&#xff0c;有些我甚至都没有用过&#xff0c;但我知道它曾经风光过。废话不多说&#xff0c;下面我要开始吹了……1、Swing下面这个是用 swing 开发的&#xff1a;Swing 算是…

如果您是JavaScript开发人员,为什么要进行增强现实-以及如何开始

by Evaristo Caraballo通过Evaristo Caraballo 如果您是JavaScript开发人员&#xff0c;为什么要进行增强现实-以及如何开始 (Why you should do Augmented Reality if you are a JavaScript developer — and how to start) If you are a JavaScript coder who is still late…

[Java 安全]加密算法

Base64编码 算法简述 定义 Base64内容传送编码是一种以任意8位字节序列组合的描述形式&#xff0c;这种形式不易被人直接识别。 Base64是一种很常见的编码规范&#xff0c;其作用是将二进制序列转换为人类可读的ASCII字符序列&#xff0c;常用在需用通过文本协议&#xff08;比…

hdu5299 Circles Game

题意是这样。给出非常多圆&#xff0c;要么两两相离&#xff0c;要么包括&#xff0c;若删掉一个圆&#xff0c;那被他包括的都要删除&#xff0c;若某人没有圆给他删&#xff0c;那么他就赢了。 。。。知道树上博弈的话。就非常easy。。。不知道的话。这确实是个神题…… 按半…

leetcode 1356. 根据数字二进制下 1 的数目排序(排序)

给你一个整数数组 arr 。请你将数组中的元素按照其二进制表示中数字 1 的数目升序排序。 如果存在多个数字二进制中 1 的数目相同&#xff0c;则必须将它们按照数值大小升序排列。 请你返回排序后的数组。 示例 1&#xff1a; 输入&#xff1a;arr [0,1,2,3,4,5,6,7,8] 输…

oracle java认证_如何通过Oracle的Java认证-开发人员实用指南

oracle java认证by javinpaul由javinpaul 如何通过Oracle的Java认证-开发人员实用指南 (How to Pass Oracle’s Java Certifications — a Practical Guide for Developers) A Java certification is highly regarded in the IT Industry and provides a Java developer with …

Oracle中exists与in的效率探讨

in 与 exist 的语法比较&#xff1a; select from 数据表 t where t.x in (...) 括号内可以是符合t.x字段类型的值集合&#xff0c;如(1,2,3)&#xff0c;但如果t.x是number类型的时候&#xff0c;似乎这样的写法会出问题&#xff1b;也可以是通 过另外的sele…

log日志轮转--logrotate

服务器上的日志包括系统日志和服务日志每天都会产生n多log,好多人会自己写脚本来进行日志的切割、压缩等&#xff0c;而忽略了系统自带的服务--logrotate。 简介 logrotate是个十分有用的工具&#xff0c;它可以自动对日志进行截断&#xff08;或轮循&#xff09;、压缩以及删除…

2个字段并在一次插入一个字段里面_elasticsearch外用与内观(二)-当插入文档时,elasticsearch都在做什么...

Previous: elasticsearch外用与内观(一)-常用功能与使用方法 在了解了es的基本用法之后&#xff0c;我们再来看看当插入文档数据时&#xff0c;elasticsearch都在做什么。首先&#xff0c;es的索引只是一个逻辑概念&#xff0c;实际上是由一个个物理分片组成的,每个分片就是一个…

学习Spring Data JPA

简介 Spring Data 是spring的一个子项目&#xff0c;在官网上是这样解释的&#xff1a; Spring Data 是为数据访问提供一种熟悉且一致的基于Spring的编程模型&#xff0c;同时仍然保留底层数据存储的特​​殊特性。它可以轻松使用数据访问技术&#xff0c;可以访问关系和非关系…

azure多功能成像好用吗_Azure持久功能简介:模式和最佳实践

azure多功能成像好用吗Authored with Steef-Jan Wiggers at Microsoft Azure由Microsoft Azure的Steef-Jan Wiggers撰写 With Durable Functions, you can program a workflow and instantiate tasks in sequential or parallel order, or you can build a watch or support a…

leetcode 327. 区间和的个数(treemap)

给定一个整数数组 nums&#xff0c;返回区间和在 [lower, upper] 之间的个数&#xff0c;包含 lower 和 upper。 区间和 S(i, j) 表示在 nums 中&#xff0c;位置从 i 到 j 的元素之和&#xff0c;包含 i 和 j (i ≤ j)。 说明: 最直观的算法复杂度是 O(n2) &#xff0c;请在此…

常用的工具函数

得到两个数组的并集, 两个数组的元素为数值或字符串//tools.js export const getUnion (arr1, arr2) > {return Array.from(new Set([...arr1, ...arr2])) }//调用页面 import { getUnion } from /libs/toolsthis.getUnion getUnion([1,2,3,5],[1,4,6]) //(6) [1, 2, 3,…

git 常用commands(转)

常用 Git 命令清单 作者&#xff1a; 阮一峰 日期&#xff1a; 2015年12月 9日 我每天使用 Git &#xff0c;但是很多命令记不住。 一般来说&#xff0c;日常使用只要记住下图6个命令&#xff0c;就可以了。但是熟练使用&#xff0c;恐怕要记住60&#xff5e;100个命令。 下面是…