用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基本拖放.......................................................................................... ...
随机推荐
- IOS内测分发策略
当苹果应用没有上交appstore审核,出于开始内侧阶段时,我们如何邀请别人测试和使用? plist苹果安装包实现 使用七牛云存储解决ios7.1的app部署问题 推荐两个现在比较流行的app内测辅助 ...
- DBCP连接池使用问题
问题现象: 启动应用,访问无压力,一切正常,一段时间过后,应用访问异常. 问题分析: 1.web容器线程爆满,拒绝服务.由于应用并发量大,线程响应时间长增加,线程池连接数逐步递增直到爆满,导致应用拒绝 ...
- 【读书笔记】.Net并行编程(三)---并行集合
为了让共享的数组,集合能够被多线程更新,我们现在(.net4.0之后)可以使用并发集合来实现这个功能.而System.Collections和System.Collections.Generic命名空 ...
- Html5 舞动的雨伞
HMTL5的学习断断续续,方法不用又生疏了,昨天做的一个雨伞的Demo,先看看效果 主要是运用了中心点变换和旋转两个方法.不同的动画用定时器控制, 下面是全部代码: <canvas id=&qu ...
- Entity Framework 5.0系列之约定配置
Code First之所以能够让开发人员以一种更加高效.灵活的方式进行数据操作有一个重要的原因在于它的约定配置.现在软件开发越来复杂,大家也都试图将软件设计的越来越灵活,很多内容我们都希望是可配置的, ...
- video.js html5 视频播放器
我个人感觉很不错 https://github.com/videojs/video.js <head> <title>Video.js | HTML5 Video Player ...
- 实战使用Axure设计App,使用WebStorm开发(6) – 迈向后端
系列文章 实战使用Axure设计App,使用WebStorm开发(1) – 用Axure描述需求 实战使用Axure设计App,使用WebStorm开发(2) – 创建 Ionic 项目 实战使 ...
- java arraylist的问题
不得不说,我犯了错,很基础的.. 遍历list的时候可以删除数组元素吗? 答案是:简单/增强for循环不可以,list.iterator()这样的方式就可以. 我之前做过类似面试题的,不过忘记了, 不 ...
- 每天一个linux命令(61):wget命令
Linux系统中的wget是一个下载文件的工具,它用在命令行下.对于Linux用户是必不可少的工具,我们经常要下载一些软件或从远程服务器恢复备份到本地服务器.wget支持HTTP,HTTPS和FTP ...
- JavaScript-分支语句练习
-1.方程 ax^2+bx+c=0,一元二次方程求根情况. 解: <head><meta http-equiv="Content-Type" content=&q ...