zoukankan      html  css  js  c++  java
  • 用redux构建购物车

    很久没更新博客了,最近要用到react,再来跟大家分享一个redux案例吧。

    1 [
    2   {"id": 1, "title": "iPad 4 Mini", "price": 500.01, "inventory": 2},
    3   {"id": 2, "title": "H&M T-Shirt White", "price": 10.99, "inventory": 10},
    4   {"id": 3, "title": "Charli XCX - Sucker CD", "price": 19.99, "inventory": 5}
    5 ]

    这是购物车里面的数据

    1 import _products from './products.json' //把json引进来
    2 
    3 const TIMEOUT = 100
    4 
    5 export default {
    6   getProducts: (cb, timeout) => setTimeout(() => cb(_products), timeout || TIMEOUT), //初始化产品
    7   buyProducts: (payload, cb, timeout) => setTimeout(() => cb(), timeout || TIMEOUT)
    8 }
    9 //1:得到产品明细 (延迟)   2:购买产品

    下面是购物车的操作

     1 import shop from '../api/shop'
     2 import * as types from '../constants/ActionTypes'
     3 
     4 const receiveProducts = products => ({
     5   type: types.RECEIVE_PRODUCTS,
     6   products: products
     7 })
     8 
     9 export const getAllProducts = () => dispatch => {
    10   shop.getProducts(products => {
    11     dispatch(receiveProducts(products))
    12   })
    13 }  //得到所有产品  从json里里面
    14 
    15 const addToCartUnsafe = productId => ({
    16   type: types.ADD_TO_CART,
    17   productId  //得到dispatch发送的数据
    18 })
    19 
    20 export const addToCart = productId => (dispatch, getState) => {
    21   if (getState().products.byId[productId].inventory > 0) {
    22     dispatch(addToCartUnsafe(productId))
    23   }
    24 } //增加产品  只有库存大于0时
    25 
    26 export const checkout = products => (dispatch, getState) => {
    27   const { cart } = getState()
    28 
    29   dispatch({
    30     type: types.CHECKOUT_REQUEST
    31   })
    32   shop.buyProducts(products, () => {
    33     dispatch({
    34       type: types.CHECKOUT_SUCCESS,
    35       cart
    36     })  //当有买入或卖出时都会checkout
    37     // Replace the line above with line below to rollback on failure:
    38     // dispatch({ type: types.CHECKOUT_FAILURE, cart })
    39   })
    40 }

    需要知道actiontypes,下面是actiontype时文件

    1 export const ADD_TO_CART = 'ADD_TO_CART'
    2 export const CHECKOUT_REQUEST = 'CHECKOUT_REQUEST'
    3 export const CHECKOUT_SUCCESS = 'CHECKOUT_SUCCESS'
    4 export const CHECKOUT_FAILURE = 'CHECKOUT_FAILURE'
    5 export const RECEIVE_PRODUCTS = 'RECEIVE_PRODUCTS'

    下面展示产品的逻辑

     1 import { combineReducers } from 'redux'
     2 import { RECEIVE_PRODUCTS, ADD_TO_CART } from '../constants/ActionTypes'
     3 
     4 const products = (state, action) => {
     5   switch (action.type) {
     6     case ADD_TO_CART:
     7       return {
     8         ...state,
     9         inventory: state.inventory - 1
    10       }
    11     default:
    12       return state
    13   }
    14 }  //往购物车里面添加一个  库存就会减少1个 注意es6语法
    15 
    16 const byId = (state = {}, action) => {
    17   switch (action.type) {
    18     case RECEIVE_PRODUCTS:
    19       return {
    20         ...state,
    21         ...action.products.reduce((obj, product) => {
    22           obj[product.id] = product
    23           return obj
    24         }, {})
    25       } // 重新初始化购物车  
    26     default:
    27       const { productId } = action
    28       if (productId) {
    29         return {
    30           ...state,
    31           [productId]: products(state[productId], action)
    32         }
    33       }
    34       return state
    35   }
    36 }
    37 
    38 const visibleIds = (state = [], action) => {
    39   switch (action.type) {
    40     case RECEIVE_PRODUCTS:
    41       return action.products.map(product => product.id)
    42     default:
    43       return state
    44   }
    45 }
    46 
    47 export default combineReducers({
    48   byId,
    49   visibleIds
    50 })  //合并reducer
    51 
    52 export const getProduct = (state, id) =>
    53   state.byId[id]
    54 
    55 export const getVisibleProducts = state =>
    56   state.visibleIds.map(id => getProduct(state, id)) //得到visible的产品

    下面再就是购物车的

     1 import {
     2   ADD_TO_CART,
     3   CHECKOUT_REQUEST,
     4   CHECKOUT_FAILURE
     5 } from '../constants/ActionTypes'
     6 
     7 const initialState = {
     8   addedIds: [],
     9   quantityById: {}
    10 }
    11 
    12 const addedIds = (state = initialState.addedIds, action) => {
    13   switch (action.type) {
    14     case ADD_TO_CART:
    15       if (state.indexOf(action.productId) !== -1) {
    16         return state
    17       }
    18       return [ ...state, action.productId ]
    19     default:
    20       return state
    21   }
    22 } //如果已添加的id没有这个  就可以添加
    23 
    24 const quantityById = (state = initialState.quantityById, action) => {
    25   switch (action.type) {
    26     case ADD_TO_CART:
    27       const { productId } = action
    28       return { ...state,
    29         [productId]: (state[productId] || 0) + 1
    30       } // 往购物车添加商品 没有就是0
    31     default:
    32       return state
    33   }
    34 }
    35 
    36 export const getQuantity = (state, productId) =>
    37   state.quantityById[productId] || 0
    38 
    39 export const getAddedIds = state => state.addedIds 
    40 
    41 const cart = (state = initialState, action) => {
    42   switch (action.type) {
    43     case CHECKOUT_REQUEST:
    44       return initialState
    45     case CHECKOUT_FAILURE:
    46       return action.cart
    47     default:
    48       return {
    49         addedIds: addedIds(state.addedIds, action),
    50         quantityById: quantityById(state.quantityById, action)
    51       }
    52   }
    53 }   
    54 
    55 export default cart

     index.js   

     1 import { combineReducers } from 'redux'
     2 import cart, * as fromCart from './cart'
     3 import products, * as fromProducts from './products'
     4 
     5 export default combineReducers({
     6   cart,
     7   products
     8 })
     9 
    10 const getAddedIds = state => fromCart.getAddedIds(state.cart)
    11 const getQuantity = (state, id) => fromCart.getQuantity(state.cart, id)
    12 const getProduct = (state, id) => fromProducts.getProduct(state.products, id) //方法调用
    13 
    14 export const getTotal = state =>
    15   getAddedIds(state)
    16     .reduce((total, id) =>
    17       total + getProduct(state, id).price * getQuantity(state, id),
    18       0
    19     )
    20     .toFixed(2)  //计算总价   
    21 
    22 export const getCartProducts = state =>
    23   getAddedIds(state).map(id => ({
    24     ...getProduct(state, id),
    25     quantity: getQuantity(state, id)  //得到购物车产品和数量
    26   }))

    cart.js

     1 import React, { PropTypes } from 'react'
     2 import Product from './Product'
     3 
     4 const Cart  = ({ products, total, onCheckoutClicked }) => {
     5   const hasProducts = products.length > 0
     6   const nodes = hasProducts ? (
     7     products.map(product =>
     8       <Product
     9         title={product.title}
    10         price={product.price}
    11         quantity={product.quantity}
    12         key={product.id}        //product组建 产品的title 数量 价格
    13       />
    14     )
    15   ) : (
    16     <em>Please add some products to cart.</em>  //无产品时提示
    17   )
    18 
    19   return (
    20     <div>
    21       <h3>Your Cart</h3>
    22       <div>{nodes}</div>
    23       <p>Total: &#36;{total}</p>
    24       <button onClick={onCheckoutClicked}
    25         disabled={hasProducts ? '' : 'disabled'}>   //购物车没产品时不能点击
    26         Checkout
    27       </button>
    28     </div>
    29   )
    30 }
    31 
    32 Cart.propTypes = {
    33   products: PropTypes.array,
    34   total: PropTypes.string,
    35   onCheckoutClicked: PropTypes.func
    36 }
    37 
    38 export default Cart
    39 Contact GitHub API Training Shop Blog About

    cartcontainer.js

     1 import React, { PropTypes } from 'react'
     2 import { connect } from 'react-redux'
     3 import { checkout } from '../actions'
     4 import { getTotal, getCartProducts } from '../reducers'
     5 import Cart from '../components/Cart'
     6 
     7 const CartContainer = ({ products, total, checkout }) => (
     8   <Cart
     9     products={products}
    10     total={total}
    11     onCheckoutClicked={() => checkout(products)} />   
    12 )
    13 
    14 CartContainer.propTypes = {
    15   products: PropTypes.arrayOf(PropTypes.shape({
    16     id: PropTypes.number.isRequired,
    17     title: PropTypes.string.isRequired,
    18     price: PropTypes.number.isRequired,
    19     quantity: PropTypes.number.isRequired
    20   })).isRequired,
    21   total: PropTypes.string,
    22   checkout: PropTypes.func.isRequired
    23 }
    24 
    25 const mapStateToProps = (state) => ({
    26   products: getCartProducts(state),  //这里得到得到的产品 就是上面的参数
    27   total: getTotal(state) //总价也一样
    28 })
    29 
    30 export default connect(
    31   mapStateToProps,
    32   { checkout }
    33 )(CartContainer)

    product.js

     1 import React, { PropTypes } from 'react'
     2 
     3 const Product = ({ price, quantity, title }) => (
     4   <div>
     5     {title} - &#36;{price}{quantity ? ` x ${quantity}` : null} //有数量就算出总价  
     6   </div>
     7 )
     8 
     9 Product.propTypes = {
    10   price: PropTypes.number,
    11   quantity: PropTypes.number,
    12   title: PropTypes.string
    13 }
    14 
    15 export default Product

    productItem.js

     1 import React, { PropTypes } from 'react'
     2 import Product from './Product'
     3 
     4 const ProductItem = ({ product, onAddToCartClicked }) => (
     5   <div style={{ marginBottom: 20 }}>
     6     <Product
     7       title={product.title}
     8       price={product.price} />
     9     <button
    10       onClick={onAddToCartClicked}
    11       disabled={product.inventory > 0 ? '' : 'disabled'}>  // 能否添加购物车
    12       {product.inventory > 0 ? 'Add to cart' : 'Sold Out'} //有库存  无库存就时soldout
    13     </button>
    14   </div>
    15 )
    16 
    17 ProductItem.propTypes = {
    18   product: PropTypes.shape({
    19     title: PropTypes.string.isRequired,
    20     price: PropTypes.number.isRequired,
    21     inventory: PropTypes.number.isRequired
    22   }).isRequired,
    23   onAddToCartClicked: PropTypes.func.isRequired
    24 }
    25 
    26 export default ProductItem

    productlist.js

     1 import React, { PropTypes } from 'react'
     2 
     3 const ProductsList = ({ title, children }) => (
     4   <div>
     5     <h3>{title}</h3>
     6     <div>{children}</div>
     7   </div>
     8 )
     9 
    10 ProductsList.propTypes = {
    11   children: PropTypes.node,
    12   title: PropTypes.string.isRequired
    13 }
    14 
    15 export default ProductsList

    productcontainer.js

     1 import React, { PropTypes } from 'react'
     2 import { connect } from 'react-redux'
     3 import { addToCart } from '../actions'
     4 import { getVisibleProducts } from '../reducers/products'
     5 import ProductItem from '../components/ProductItem'
     6 import ProductsList from '../components/ProductsList'
     7 
     8 const ProductsContainer = ({ products, addToCart }) => (
     9   <ProductsList title="Products">
    10     {products.map(product =>
    11       <ProductItem
    12         key={product.id}
    13         product={product}
    14         onAddToCartClicked={() => addToCart(product.id)} />  //这就是  children
    15     )}
    16   </ProductsList>
    17 )
    18 
    19 ProductsContainer.propTypes = {
    20   products: PropTypes.arrayOf(PropTypes.shape({
    21     id: PropTypes.number.isRequired,
    22     title: PropTypes.string.isRequired,
    23     price: PropTypes.number.isRequired,
    24     inventory: PropTypes.number.isRequired
    25   })).isRequired,
    26   addToCart: PropTypes.func.isRequired
    27 }
    28 
    29 const mapStateToProps = state => ({
    30   products: getVisibleProducts(state.products) //products的来源
    31 })
    32 
    33 export default connect(
    34   mapStateToProps,
    35   { addToCart }
    36 )(ProductsContainer)

    app.js

     1 import React from 'react'
     2 import ProductsContainer from './ProductsContainer'
     3 import CartContainer from './CartContainer'
     4 
     5 const App = () => (
     6   <div>
     7     <h2>Shopping Cart Example</h2>
     8     <hr/>
     9     <ProductsContainer />
    10     <hr/>
    11     <CartContainer />
    12   </div>
    13 )
    14 
    15 export default App

    到这里应该差不多看明白了,组件还是很容易看的,主要是action之间的衔接确实。。。

    index.html

     1 import React from 'react'
     2 import { render } from 'react-dom'
     3 import { createStore, applyMiddleware } from 'redux'
     4 import { Provider } from 'react-redux'
     5 import createLogger from 'redux-logger'
     6 import thunk from 'redux-thunk'
     7 import reducer from './reducers'
     8 import { getAllProducts } from './actions'
     9 import App from './containers/App'
    10 
    11 const middleware = [ thunk ];
    12 if (process.env.NODE_ENV !== 'production') {
    13   middleware.push(createLogger());
    14 }
    15 
    16 const store = createStore(
    17   reducer,
    18   applyMiddleware(...middleware)
    19 )
    20 
    21 store.dispatch(getAllProducts())  //初始化产品
    22 
    23 render(
    24   <Provider store={store}>
    25     <App />
    26   </Provider>,
    27   document.getElementById('root')
    28 )

    在我看来 redux的精华就是reducers,这点也是很难掌握的,组件到比较容易,今后多多研究reducer和action。

  • 相关阅读:
    8086汇编中的逻辑地址与物理地址转换
    wepy开发踩坑记录
    cordova开发的坑
    express转发请求
    Hybrid app(cordova) 环境配置记录
    laravel-mix 热重载404的问题
    练习
    git 使用记录
    Vue全家桶开发笔记
    微信小程序开发踩坑记录
  • 原文地址:https://www.cnblogs.com/yoissee/p/5961841.html
Copyright © 2011-2022 走看看