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.pswp.cn/news/392713.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/392713.shtml
英文地址,請注明出處:http://en.pswp.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個命令。 下面是…