Reduxのサンプルのショッピングカートをステップ・バイ・ステップで Part.3のつづき。
1.カートに合計額を表示する
カート内の合計額を算出する getTotal関数を追加
export const getTotal = state =>{
return getAddedIds(state)
.reduce(
(total,id) => total +getProduct(state,id).price*getQuantity(state,id),
0
).toFixed();
}
CartContainerコンテナにプロップスを追加
getTotalで算出した合計額をtotalプロップスとしてコンテナに渡す。コンテナ内部ではCartコンポーネントにtotalプロップスを渡している。
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { getCartProducts , getTotal} from '../reducers'
import Cart from '../components/Cart'
const CartContainer = ({ products,total}) => (
<Cart
products={products}
total={total}
/>
)
CartContainer.propTypes = {
products: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
price: PropTypes.number.isRequired,
quantity: PropTypes.number.isRequired
})).isRequired,
total:PropTypes.string.isRequired
}
const mapStateToProps = (state) => ({
products: getCartProducts(state),
total:getTotal(state)
})
export default connect(mapStateToProps)(CartContainer)
Cartコンポーネントの編集
CartContainerコンテナから渡されたtotalプロップスに対応させる。合計額表示DOMを追加。
import React from 'react'
import PropTypes from 'prop-types'
import Product from './Product'
const Cart = ({ products,total}) => {
const hasProducts = products.length > 0
const nodes = hasProducts ? (
products.map(product =>
<Product
title={product.title}
price={product.price}
quantity={product.quantity}
key={product.id}
/>
)
) : (
<em>ご希望の商品をカートに追加してください</em>
)
return (
<div className="border border-dark alert alert-secondary rounded p-1 clearfix mx-auto" style={{width:'30em'}}>
<h5 className="mb-1 text-center">🛒ショッピングカート</h5>
<div>{nodes}</div>
<p className="text-white bg-dark text-center" style={{ marginLeft: "20em" }}>合計: ¥{total}</p>
</div>
)
}
Cart.propTypes = {
products: PropTypes.array,
total:PropTypes.string
}
export default Cart
実行結果
2.決済機能を付ける
ほとんど、見た目だけのなんちゃって決済機能。
アクションのtype追加
export const RECEIVE_PRODUCTS = 'RECEIVE_PRODUCTS'
export const ADD_TO_CART = 'ADD_TO_CART'
export const CHECKOUT_REQUEST = 'CHECKOUT_REQUEST'
export const CHECKOUT_SUCCESS = 'CHECKOUT_SUCCESS'
export const CHECKOUT_FAILURE = 'CHECKOUT_FAILURE'
アクションに対応するdispatchをする関数追加
checkout関数を追加。
これはCHECKOUT_REQUESTアクションと、CHECKOUT_SUCCESSアクションをディスパッチする。
shop.buyProducts関数は、内部で無条件に決済成功した場合のアクションをディスパッチしている。
実際は、サーバーからの返答を受け取ってから、成功の場合と失敗の場合の両方に対応すべき。
import shop from '../api/shop'
import * as types from '../constants/ActionTypes'
import {getTotal} from '../reducers'
//(中略....................)
//実行時、dとgにはdispatchとgetStateが割り当てられる。
//g すなわち getState() は state tree を返す。
export const checkout = products => (d, g) => {
// stateからstate.cartだけ取り出し
const { cart } = g();
const paid = getTotal(g())
d({
type: types.CHECKOUT_REQUEST
});
shop.buyProducts(
products,
//とりあえずここでは、無条件に成功するようにしておく。
//このコールバック関数は、サーバー側の決済処理が成功すれば、成功のdispatch
// 失敗すれば失敗のdispatchをする関数にするべきだろう
() => {
d({
type: types.CHECKOUT_SUCCESS,
paid,
cart
})
}
// () => {
// dispatch({
// type: types.CHECKOUT_FAILURE,
// cart
// })
// }
)
}
cartリデューサを修正
CHECKOUT_REQUEST, CHECKOUT_SUCCESS, CHECKOUT_FAILUREアクションに対応させた。
CHECKOUT_SUCCESSのときは、コンソールに支払い金額を表示する。
import {
CHECKOUT_REQUEST,
CHECKOUT_FAILURE,
CHECKOUT_SUCCESS,
ADD_TO_CART
} from "../constants/ActionTypes";
//中略....
//cartリデューサの実体
//内部で addedIds, quantityById リデューサをひとまとめにして利用している
const cart = (state = initialState, action) => {
switch (action.type) {
case CHECKOUT_REQUEST:
return initialState;
case CHECKOUT_FAILURE:
console.log("決済に失敗しました。");
return action.cart
// ADD_TO_CARTはこちら
case CHECKOUT_SUCCESS:
console.log("決済が完了しました。");
console.log("お支払い額:¥"+action.paid);
return initialState;
// ADD_TO_CARTはこちら
default:
return {
addedIds: addedIds(state.addedIds, action),
quantityById: quantityById(state.quantityById, action)
}
}
}
export default cart
実行結果
Reduxのサンプルのショッピングカートをステップ・バイ・ステップで Part.4-2 https://t.co/2tedVTJLPB @YouTubeさんから
— tkarasuma (@tkarasuma) 2018年6月14日
これで、"Reduxのサンプルのショッピングカートをステップ・バイ・ステップ" の終わり。
