用redux构建购物车
很久没更新博客了,最近要用到react,再来跟大家分享一个redux案例吧。
[
{"id": 1, "title": "iPad 4 Mini", "price": 500.01, "inventory": 2},
{"id": 2, "title": "H&M T-Shirt White", "price": 10.99, "inventory": 10},
{"id": 3, "title": "Charli XCX - Sucker CD", "price": 19.99, "inventory": 5}
]
这是购物车里面的数据
import _products from './products.json' //把json引进来
const TIMEOUT = 100
export default {
getProducts: (cb, timeout) => setTimeout(() => cb(_products), timeout || TIMEOUT), //初始化产品
buyProducts: (payload, cb, timeout) => setTimeout(() => cb(), timeout || TIMEOUT)
}
//1:得到产品明细 (延迟) 2:购买产品
下面是购物车的操作
import shop from '../api/shop'
import * as types from '../constants/ActionTypes' const receiveProducts = products => ({
type: types.RECEIVE_PRODUCTS,
products: products
}) export const getAllProducts = () => dispatch => {
shop.getProducts(products => {
dispatch(receiveProducts(products))
})
} //得到所有产品 从json里里面 const addToCartUnsafe = productId => ({
type: types.ADD_TO_CART,
productId //得到dispatch发送的数据
}) export const addToCart = productId => (dispatch, getState) => {
if (getState().products.byId[productId].inventory > 0) {
dispatch(addToCartUnsafe(productId))
}
} //增加产品 只有库存大于0时 export const checkout = products => (dispatch, getState) => {
const { cart } = getState() dispatch({
type: types.CHECKOUT_REQUEST
})
shop.buyProducts(products, () => {
dispatch({
type: types.CHECKOUT_SUCCESS,
cart
}) //当有买入或卖出时都会checkout
// Replace the line above with line below to rollback on failure:
// dispatch({ type: types.CHECKOUT_FAILURE, cart })
})
}
需要知道actiontypes,下面是actiontype时文件
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'
export const RECEIVE_PRODUCTS = 'RECEIVE_PRODUCTS'
下面展示产品的逻辑
import { combineReducers } from 'redux'
import { RECEIVE_PRODUCTS, ADD_TO_CART } from '../constants/ActionTypes'
const products = (state, action) => {
switch (action.type) {
case ADD_TO_CART:
return {
...state,
inventory: state.inventory - 1
}
default:
return state
}
} //往购物车里面添加一个 库存就会减少1个 注意es6语法
const byId = (state = {}, action) => {
switch (action.type) {
case RECEIVE_PRODUCTS:
return {
...state,
...action.products.reduce((obj, product) => {
obj[product.id] = product
return obj
}, {})
} // 重新初始化购物车
default:
const { productId } = action
if (productId) {
return {
...state,
[productId]: products(state[productId], action)
}
}
return state
}
}
const visibleIds = (state = [], action) => {
switch (action.type) {
case RECEIVE_PRODUCTS:
return action.products.map(product => product.id)
default:
return state
}
}
export default combineReducers({
byId,
visibleIds
}) //合并reducer
export const getProduct = (state, id) =>
state.byId[id]
export const getVisibleProducts = state =>
state.visibleIds.map(id => getProduct(state, id)) //得到visible的产品
下面再就是购物车的
import {
ADD_TO_CART,
CHECKOUT_REQUEST,
CHECKOUT_FAILURE
} from '../constants/ActionTypes'
const initialState = {
addedIds: [],
quantityById: {}
}
const addedIds = (state = initialState.addedIds, action) => {
switch (action.type) {
case ADD_TO_CART:
if (state.indexOf(action.productId) !== -1) {
return state
}
return [ ...state, action.productId ]
default:
return state
}
} //如果已添加的id没有这个 就可以添加
const quantityById = (state = initialState.quantityById, action) => {
switch (action.type) {
case ADD_TO_CART:
const { productId } = action
return { ...state,
[productId]: (state[productId] || 0) + 1
} // 往购物车添加商品 没有就是0
default:
return state
}
}
export const getQuantity = (state, productId) =>
state.quantityById[productId] || 0
export const getAddedIds = state => state.addedIds
const cart = (state = initialState, action) => {
switch (action.type) {
case CHECKOUT_REQUEST:
return initialState
case CHECKOUT_FAILURE:
return action.cart
default:
return {
addedIds: addedIds(state.addedIds, action),
quantityById: quantityById(state.quantityById, action)
}
}
}
export default cart
index.js
import { combineReducers } from 'redux'
import cart, * as fromCart from './cart'
import products, * as fromProducts from './products'
export default combineReducers({
cart,
products
})
const getAddedIds = state => fromCart.getAddedIds(state.cart)
const getQuantity = (state, id) => fromCart.getQuantity(state.cart, id)
const getProduct = (state, id) => fromProducts.getProduct(state.products, id) //方法调用
export const getTotal = state =>
getAddedIds(state)
.reduce((total, id) =>
total + getProduct(state, id).price * getQuantity(state, id),
0
)
.toFixed(2) //计算总价
export const getCartProducts = state =>
getAddedIds(state).map(id => ({
...getProduct(state, id),
quantity: getQuantity(state, id) //得到购物车产品和数量
}))
cart.js
import React, { PropTypes } from 'react'
import Product from './Product'
const Cart = ({ products, total, onCheckoutClicked }) => {
const hasProducts = products.length > 0
const nodes = hasProducts ? (
products.map(product =>
<Product
title={product.title}
price={product.price}
quantity={product.quantity}
key={product.id} //product组建 产品的title 数量 价格
/>
)
) : (
<em>Please add some products to cart.</em> //无产品时提示
)
return (
<div>
<h3>Your Cart</h3>
<div>{nodes}</div>
<p>Total: ${total}</p>
<button onClick={onCheckoutClicked}
disabled={hasProducts ? '' : 'disabled'}> //购物车没产品时不能点击
Checkout
</button>
</div>
)
}
Cart.propTypes = {
products: PropTypes.array,
total: PropTypes.string,
onCheckoutClicked: PropTypes.func
}
export default Cart
Contact GitHub API Training Shop Blog About
cartcontainer.js
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { checkout } from '../actions'
import { getTotal, getCartProducts } from '../reducers'
import Cart from '../components/Cart'
const CartContainer = ({ products, total, checkout }) => (
<Cart
products={products}
total={total}
onCheckoutClicked={() => checkout(products)} />
)
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,
checkout: PropTypes.func.isRequired
}
const mapStateToProps = (state) => ({
products: getCartProducts(state), //这里得到得到的产品 就是上面的参数
total: getTotal(state) //总价也一样
})
export default connect(
mapStateToProps,
{ checkout }
)(CartContainer)
product.js
import React, { PropTypes } from 'react'
const Product = ({ price, quantity, title }) => (
<div>
{title} - ${price}{quantity ? ` x ${quantity}` : null} //有数量就算出总价
</div>
)
Product.propTypes = {
price: PropTypes.number,
quantity: PropTypes.number,
title: PropTypes.string
}
export default Product
productItem.js
import React, { PropTypes } from 'react'
import Product from './Product'
const ProductItem = ({ product, onAddToCartClicked }) => (
<div style={{ marginBottom: 20 }}>
<Product
title={product.title}
price={product.price} />
<button
onClick={onAddToCartClicked}
disabled={product.inventory > 0 ? '' : 'disabled'}> // 能否添加购物车
{product.inventory > 0 ? 'Add to cart' : 'Sold Out'} //有库存 无库存就时soldout
</button>
</div>
)
ProductItem.propTypes = {
product: PropTypes.shape({
title: PropTypes.string.isRequired,
price: PropTypes.number.isRequired,
inventory: PropTypes.number.isRequired
}).isRequired,
onAddToCartClicked: PropTypes.func.isRequired
}
export default ProductItem
productlist.js
import React, { PropTypes } from 'react'
const ProductsList = ({ title, children }) => (
<div>
<h3>{title}</h3>
<div>{children}</div>
</div>
)
ProductsList.propTypes = {
children: PropTypes.node,
title: PropTypes.string.isRequired
}
export default ProductsList
productcontainer.js
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { addToCart } from '../actions'
import { getVisibleProducts } from '../reducers/products'
import ProductItem from '../components/ProductItem'
import ProductsList from '../components/ProductsList'
const ProductsContainer = ({ products, addToCart }) => (
<ProductsList title="Products">
{products.map(product =>
<ProductItem
key={product.id}
product={product}
onAddToCartClicked={() => addToCart(product.id)} /> //这就是 children
)}
</ProductsList>
)
ProductsContainer.propTypes = {
products: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
price: PropTypes.number.isRequired,
inventory: PropTypes.number.isRequired
})).isRequired,
addToCart: PropTypes.func.isRequired
}
const mapStateToProps = state => ({
products: getVisibleProducts(state.products) //products的来源
})
export default connect(
mapStateToProps,
{ addToCart }
)(ProductsContainer)
app.js
import React from 'react'
import ProductsContainer from './ProductsContainer'
import CartContainer from './CartContainer' const App = () => (
<div>
<h2>Shopping Cart Example</h2>
<hr/>
<ProductsContainer />
<hr/>
<CartContainer />
</div>
) export default App
到这里应该差不多看明白了,组件还是很容易看的,主要是action之间的衔接确实。。。
index.html
import React from 'react'
import { render } from 'react-dom'
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import createLogger from 'redux-logger'
import thunk from 'redux-thunk'
import reducer from './reducers'
import { getAllProducts } from './actions'
import App from './containers/App' const middleware = [ thunk ];
if (process.env.NODE_ENV !== 'production') {
middleware.push(createLogger());
} const store = createStore(
reducer,
applyMiddleware(...middleware)
) store.dispatch(getAllProducts()) //初始化产品 render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
在我看来 redux的精华就是reducers,这点也是很难掌握的,组件到比较容易,今后多多研究reducer和action。
用redux构建购物车的更多相关文章
- react+redux构建淘票票首页
react+redux构建淘票票首页 描述 在之前的项目中都是单纯的用react,并没有结合redux.对于中小项目仅仅使用react是可以的:但当项目变得更加复杂,仅仅使用react是远远不够的,我 ...
- Redux初见
说到redux可能我们都先知道了react,但我发现,关于react相关的学习资料很多,也有各种各样的种类,但是关于redux简单易懂的资料却比较少. 这里记录一下自己的学习理解,希望可以简洁易懂,入 ...
- Redux状态管理方法与实例
状态管理是目前构建单页应用中不可或缺的一环,也是值得花时间学习的知识点.React官方推荐我们使用Redux来管理我们的React应用,同时也提供了Redux的文档来供我们学习,中文版地址为http: ...
- Redux生态系统
生态系统 Redux 是一个体小精悍的库,但它相关的内容和 API 都是精挑细选的,足以衍生出丰富的工具集和可扩展的生态系统. 如果需要关于 Redux 所有内容的列表,推荐移步至 Awesome R ...
- ApacheCN Asp.NET 译文集 20211126 更新
ASP.NET Core2 基础知识 零.前言 一.搭建舞台 二.控制器 三.视图 四.模型 五.验证 六.路由 七.RestBuy 八.添加功能.测试和部署 ASP.NET Core3 和 Angu ...
- Jquery easyui 教程
Jquery easyui教程 目 录 1基本拖放... 4 2构建购物车型拖放... 5 3创建课程表... 8 4菜单和按钮Menu and Bu ...
- 初识React-Redux之粗暴理解入门
权当暂记 日后再行补充完善,若有阅读者,请翻到下文黄色标题'从这里开始'起阅读. Rudex在我看来最本质做的事情就是将所有的State属性统一存储(一个属性就是一个注册到store的Reducer) ...
- mvp在flutter中的应用
mvp模式的优点mvp模式将视图.业务逻辑.数据模型隔离,使用mvp模式,能使复杂的业务逻辑变得更加清晰,使代码更具有灵活性和扩展性,正是这些优点,使mvp模式广泛应用于原生开发中. flutter使 ...
- Jquery easyui教程
目 录 1基本拖放.......................................................................................... ...
随机推荐
- 使用WMI和性能计数器监控远程服务器权限设置
应用场景:在web服务器中,通过.NET编码使用WMI查询远程服务器的一些硬件配置信息,使用性能计数器查询远程机器的运行时资源使用情况.在网上没有找到相关的东西,特记录与大家共享. 将web服务器和所 ...
- PowerShell笔记
教程: http://www.pstips.net/powershell-online-tutorials/ http://www.3fwork.com/a113/ 1. 查看成员 $MyInvoca ...
- java 多线程(threadlocal)
package com.example; import java.util.Random; public class App { public static class MyRunnable1 imp ...
- Java框架介绍-13个不容错过的框架项目
本文转自互联网,个人收藏所用. 下面,我们将一同分享各有趣且颇为实用的Java库,大家请任取所需.不用客气~ 1.极致精简的Java Bootique是一项用于构建无容器可运行Java应用的极简技术. ...
- CSS折行小记
最近项目中遇到字段内容过长的显示问题. 后来在网上找了资料,http://www.php100.com/html/webkaifa/DIV_CSS/2009/0416/2453.html 用了td( ...
- Oracle数据库面试题【转载】
1. Oracle跟SQL Server 2005的区别? 宏观上: 1). 最大的区别在于平台,oracle可以运行在不同的平台上,sql server只能运行在windows平台上,由于windo ...
- MVVM架构~knockoutjs系列之为Ajax传递Ko数组对象
返回目录 一些要说的 这是一个很有意思的题目,在KO里,有对象和数组对象两种,但这两种对象对外表现都是一个function,如果希望得到他的值,需要进行函数式调用,如ko_a(),它的结果为一个具体值 ...
- iptables基础命令详解
TCP/IP基本概念: TCP/IP将网络分为四层:应用层,传输层,网络层,链路层. 传输层:定义了两种通信协议,分别为TCP协议和UDP协议. TCP协议:TCP协议在传输 数据过程中会检查数据的完 ...
- iOS----Asset Catalog的用法
文/余书懿(简书作者)原文链接:http://www.jianshu.com/p/7aa06ce22a7b著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. 引言 Asset Catalo ...
- KnockoutJS 3.X API 第三章 计算监控属性(3) KO如何实现依赖追踪
KO是如何实现自动更新的 初学者可以掠过该篇,如果你是一个刨根问底的开发者,那本节将告诉你KO是如何实现依赖追踪和UI自动更新的. 其实很简单,KO的依赖追踪算法如下: 当你声明一个计算监控属性,KO ...