用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基本拖放.......................................................................................... ...
随机推荐
- 消息智能路由组件SmartRoute
消息传递在软件开发过程中是一件很常见的事情,而在不同的场景所使用消息传递方式也有所不同,在对象之间制定相关接口方法和对象结构,对于进程之间可能使用内存共享或一些通讯产品,在不同服务器之的消息通讯则使用 ...
- Java提高篇(三一)-----Stack
在Java中Stack类表示后进先出(LIFO)的对象堆栈.栈是一种非常常见的数据结构,它采用典型的先进后出的操作方式完成的.每一个栈都包含一个栈顶,每次出栈是将栈顶的数据取出,如下: Stack通过 ...
- [Beautifulzzzz的博客目录] 快速索引点这儿O(∩_∩)O~~,红色标记的是不错的(⊙o⊙)哦~
3D相关开发 [direct-X] 1.direct-X最小框架 [OpenGL] 1.环境搭建及最小系统 [OpenGL] 2.企业版VC6.0自带的Win32-OpenGL工程浅析 51单片机 [ ...
- jQuery.width()和jQuery.css('width')的区别
[TOC] 问题描述 使用jQuery修改一个div的宽度时,发现$($0).width('10rem')总是修改成不正确的值,然后使用$($0).css('width', '10rem')时却能正确 ...
- Microsoft Dynamics CRM 2013 and 2011 Update Rollups and Service Packs
Microsoft Dynamics CRM 2013 BTW: RC stands for Release for Candidate, and RTM stands for Release ...
- xml存储bug
最近遇到了一个bug,详细情况如下:用linq to xml写xml文件,在加载的时候代码为xDocument.Load(filePath),保存的时候为xDocument.Save(filePath ...
- 简单总结java 语法
通过学习慢慢的爱上了这门语言,在Java的学习过程中,可能会遇到形形色色的问题不容易解决,应多去专业论坛了解相关的知识,书本上的知识有限.要会从网上搜索有用的信息加以整理,促进学习的深入和知识水平的提 ...
- [JavaWeb]关于DBUtils中QueryRunner的一些解读.
前言:[本文属于原创分享文章, 转载请注明出处, 谢谢.]前面已经有文章说了DBUtils的一些特性, 这里再来详细说下QueryRunner的一些内部实现, 写的有错误的地方还恳请大家指出. Que ...
- fir.im Weekly - 论个人技术影响力是如何炼成的
每个圈子都有一群能力强且懂得经营自己的人,技术圈也是如此.本期 fir.im Weekly 一如往期精选了一些实用的 iOS,Android 开发工具和源码分享,还有一些关于程序员的成长 Tips 和 ...
- 使用JSExcelXML.js导出Excel模板
github地址:https://github.com/464884492/JSExcelXml 业务系统显示效果图 导出模板图 功能描述 世间万物总是相生相克,既然我们的客户要求有导出Ex ...