React-Redux总结含购物车案例

React-Redux总结含购物车案例

reduc简介

redux是react全家桶的一员,它为react给i共可预测化的状态管理机制。redux是将整个应用状态存储到一个地方,成为store,里面存放着一颗树状态(state,tree),组件可以派发dispatch行为action给store,而不是直接通知其他组件,其他组件可以通过订阅store中的状态state来刷新自己的视图。

主要的四个特征:

  • 可预测:reducer是纯函数,所有状态是可预测的。
  • 易调试:全局只有一个store。
  • 灵活性:action,修改state。
  • 中心化:middleware机制,源码简介,扩展生态丰富。

设计思想

redux是一种状态管理库,用于管理React应用中的全局状态,核心思想是将应用的状态集中存储在一个全局的Store中,使得状态的变化可追溯,可控制,可预测。

  • 单一的数据源:redux倡导使用单一的数据源的方式来管理应用的状态,即整个应用的状态存储再一个全局的JavaScript对象中,这有助于简单化状态管理的逻辑,使得状态的变化是可预测易于调试的。
  • 不可变性:redux状态是不可变的,即状态一旦创建就不可以修改,每次状态发生变化时,都会生成一个新的状态对象,而不是直接修改原有的状态。这有助于避免状态的不一致和难以追溯的bug。

什么情况下使用redux

  • 某个组件的状态,需要让其他组件可以随时拿到(共享)。
  • 一个组件需要改变另一个组件的状态(通信)。
  • 总体原则是能不用就不用,如果不用比较吃力考虑使用。

redux工作流程

component --> dispatch(action) --> reducer --> subscribe --> getState --> component
  • 1.触发Action:应用中的某个事件或用户行为触发一个Action,Action是一个包含type属性和可选的payload属性通过JavaScript对象,用于描述状态的变化。
  • 2.派发Action:通过调用Redux的dispatch(action)方法将Action派发到Redux的Store中。
  • 3.处理Reducer:Store接收到Action后,会调用所有注册的Reducer函数,将当前的状态和Action传入Reducer中。
  • 4.更新状态:Reducer根据Action的类型,处理状态的变化逻辑,并返回一个新的状态,Redux会将新的状态替代原有的状态,从而更新整个应用的状态。
  • 5.通知订阅者:状态更新后,Redux会通过subScribe(listener)方法注册的监听器,让他们执行相应的回调函数,从而实现对状态变化的监听和响应。

redux的三个核心概念

action

对象,描述要做的事情,项目中的每一个都是一个action

	语法:{type:"命令",payload:"载荷"}

特点:

  • i.只描述做什么。
  • ii.js对象,必须带有type属性,用于区分动作的类型。
  • iii.根据功能的不同,可以携带额外的数据,配合该数据来完成相应的功能。

reducer

函数,用来处理action并更新状态,是Redux状态更新的地方

语法:函数签名:'(prevState,action)=>newState'
const reducer = (state, action) => {switch(action.type){case 'ADD':state['sum'] = action.datareturn [...state]break...}
}

特点:

  • i.注意该函数一定会有返回值,即使状态没有改变也要返回上一次的状态。
  • ii.约定reducer是一个纯函数,并不能包含side effect副作用,例如,不能修改函数参数,不能修改函数外部数据,不能进行异步操作等。
  • iii.对于reducer来说,为了保证reducer是一个纯函数,不要直接修改参数state的值,也就是不要直接修改当前状态,而是根据当前状态值创建新的状态值,不要使用Math.random()/new
    Date()/Date.new()/ajax请求等不纯的操作,不要让reducer执行副作用sideEffect

store

仓库,redux的核心,整合action和reducer

	import {createStore} from 'redux'let store = createStore(reducer)

特点:

  • i.一个应用只有一个store
  • ii.维护应用的状态,获取状态:store.getState()
  • iii.发起状态更新时,store.dispatch(action) iv.创建store时,接收reducer作为参数,const
    store = create Store(reducer)

其他API:

  • i.订阅监听状态变化,const unSubscribe = store.subscribe(()=>{})
  • ii.取消订阅状态变化,unSubscribe()

react-redux概述

概念

在react-redux中,有两个核心概念,即Provider和connect。provider是一个React组件,用于将Redux的Store传递给React应用中的所有组件,从而是的组件可以访问到全局的状态。connect是一个高阶函数,用于将React组件连接到Redux的store,从而实现组件与Redux
store之间的数据传递和状态管理。

如何将React组件连接到Redux store

使用connect函数可以将React组件连接到Redux的store。通过在组件定义时调用connect函数,并传入需要的参数和回调函数,可以将组件与Redux的store进行连接,连接后,组件可以通过prop访问到Redux
Store中的状态,并且可以向Redux store派发action 来修改全局状态。

如何使用React-redux的高阶组件和hooks来简化代码:

react-redux提供了一些高阶组件和hooks,可以帮助简化组件与Redux store之间的交互代码,例如,mapStateToProps和mapDispatchToProps参数可以帮助组件定义如何从redux store中获取状态和派发action的方式,从而减少了在组件中处理Redux store的繁琐代码,此外,React-redux还提供了一些hooks,例如useSelector和useDispatch,可以在函数组件中更方便的访问Redux store的状态和派发action。

react-redux购物车案例
效果:
React-Redux总结含购物车案例

目录结构:
react-redux购物车案例
action/index.js代码

import { ADD_PRODUCT, REMOVE_PRODUCT } from "../constants";export const addProduct = id => ({type: ADD_PRODUCT,payload: id
});export const removeProduct = id => ({type: REMOVE_PRODUCT,payload: id
});cart/cartInte.js代码
import React from "react";
import PropTypes from "prop-types";const CartItem = ({ name, price, quantity, itemTotal, removeProduct }) => (<div className="cartWrapper"><div>{name}</div><div>${price}</div><div>x{quantity}</div><div>= ${itemTotal}</div><div><button onClick={() => removeProduct(name)}>Remove</button></div></div>
);CartItem.propTypes = {name: PropTypes.string.isRequired,price: PropTypes.string.isRequired,quantity: PropTypes.number.isRequired,itemTotal: PropTypes.string.isRequired,removeProduct: PropTypes.func.isRequired
};
export default CartItem;

cart/index.js代码

import React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import "./Cart.css";
import CartItem from "./CartItem";
import { getCart, getCartTotal } from "../../reducers";
import { removeProduct } from "../../actions";const Cart = ({ cart, cartTotal, removeProduct }) => (<React.Fragment><h2>Checkout Cart</h2>{cart.map(({ name, price, quantity, itemTotal }) => (<CartItemkey={name}name={name}price={price}quantity={quantity}itemTotal={itemTotal}removeProduct={removeProduct}/>))}<h2>Total</h2><div className="total">${cartTotal}</div></React.Fragment>
);Cart.propTypes = {cart: PropTypes.arrayOf(PropTypes.shape({name: PropTypes.string,price: PropTypes.string,quantity: PropTypes.number,itemTotal: PropTypes.string})),cartTotal: PropTypes.string,removeProduct: PropTypes.func.isRequired
};const mapStateToProps = state => ({cart: getCart(state),cartTotal: getCartTotal(state)
});
export default connect(mapStateToProps, { removeProduct })(Cart);

cart/Cart.css代码

.cartWrapper {display: grid;grid-template-columns: 150px 150px 100px 100px 200px;grid-gap: 10px;white-space: nowrap;
}.total {text-decoration: underline;
}

ProductList/index.js代码

import React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import "./ProductList.css";
import Product from "./Product";
import { getProducts } from "../../reducers";
import { addProduct } from "../../actions";const ProductList = ({ products, addProduct }) => (<React.Fragment><h2>Product List</h2>{products.map(({ name, price }) => (<Product key={name} name={name} price={price} addProduct={addProduct} />))}</React.Fragment>
);
// propTypes验证,在给react组件传属性的的时候,定义属性的类型
ProductList.propTypes = {products: PropTypes.arrayOf(PropTypes.shape({name: PropTypes.string.isRequired,price: PropTypes.string.isRequired})),addProduct: PropTypes.func.isRequired
};const mapStateToProps = state => {return { products: getProducts(state) };
};export default connect(mapStateToProps, { addProduct })(ProductList); 

ProductList/product.js代码

import React from "react";
import PropTypes from "prop-types";const Product = ({ name, price, addProduct }) => (<div className="productWrapper"><div>{name}</div><div>{price}</div><div><button onClick={() => addProduct(name)}>Add</button></div></div>
);Product.propTypes = {name: PropTypes.string.isRequired,price: PropTypes.string.isRequired,addProduct: PropTypes.func.isRequired
};export default Product;

ProductList/product.css代码

.productWrapper {display: grid;grid-template-columns: 150px 150px 300px;grid-gap: 10px;
}

reducers/cart.js代码

import { combineReducers } from "redux";
import { ADD_PRODUCT, REMOVE_PRODUCT } from "../constants";//state = [id1, id2]
const initialCartAllIds = [];
// Reducer(状态处理函数)
const cartAllIds = (state = initialCartAllIds, action) => {switch (action.type) {case ADD_PRODUCT: {const newItem = action.payload;if (state.includes(newItem)) return state;return [...state, action.payload];}case REMOVE_PRODUCT: {const unwantedItem = action.payload;return state.filter(item => item !== unwantedItem);}default:return state;}
};// state={id: {quantity: productQuantity}}
const initialCartById = {};
const cartById = (state = initialCartById, action) => {switch (action.type) {case ADD_PRODUCT: {const newItem = action.payload;const newQuantity = state[newItem] ? state[newItem].quantity + 1 : 1;return { ...state, [newItem]: { quantity: newQuantity } };}case REMOVE_PRODUCT: {const unwantedItem = action.payload;const newState = { ...state };delete newState[unwantedItem];return newState;}default:return state;}
};export const cart = combineReducers({cartAllIds,cartById
});export const getCart = (products, cart) => {return cart.cartAllIds.map(productName => {const name = productName;const price = products.productById[productName].price;const quantity = cart.cartById[productName].quantity;const itemTotal = (price * quantity).toFixed(2);return { name, price, quantity, itemTotal };});
};export const getCartTotal = (products, cart) => {return cart.cartAllIds.reduce((pre, cur) => {const price = products.productById[cur].price;const quantity = cart.cartById[cur].quantity;return pre + price * quantity;}, 0).toFixed(2);
};

reducers/Products.js代码

import { combineReducers } from "redux";
import productsData from "../data";// product ID is the product name in this case
// state = {[id]:{name: productName, price: productPrice}}
const initialProductById = (function() {const state = {};productsData.forEach(({ name, price }) => (state[name] = { name, price: price.toFixed(2) }));return state;
})();
const productById = (state = initialProductById, action) => {switch (action.type) {default:return state;}
};// state = [id1, id2]
const initialProductAllIds = productsData.map(product => product.name);
const productAllIds = (state = initialProductAllIds, action) => {switch (action.type) {default:return state;}
};export const products = combineReducers({productById,productAllIds
});export const getProducts = products => {return products.productAllIds.map(key => products.productById[key]);
};

reducers/index.js代码

import { combineReducers } from "redux";
import * as productReducer from "./products";
import * as cartReducer from "./cart";const reducer = combineReducers({products: productReducer.products,cart: cartReducer.cart
});export const getProducts = state => productReducer.getProducts(state.products);export const getCart = state => cartReducer.getCart(state.products, state.cart);export const getCartTotal = state =>cartReducer.getCartTotal(state.products, state.cart);export default reducer;

configureStore.js代码

import { createStore } from "redux";
import reducer from "./reducers";
import { loadState, saveState } from "./localStorage";const persistedState = loadState();const store = createStore(reducer,persistedState,window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);store.subscribe(() => {const cartValue = store.getState().cart;saveState({ cart: cartValue });
});export default store;

constants.js代码

export const ADD_PRODUCT = "ADD_PRODUCT";
export const REMOVE_PRODUCT = "REMOVE_PRODUCT";

数据源data.js代码

const products = [{name: "Sledgehammer",price: 125.75},{name: "Axe",price: 190.5},{name: "Bandsaw",price: 562.13},{name: "Chisel",price: 12.9},{name: "Hacksaw",price: 18.45}
];export default products;

index.js中引入store

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import App from "./components/App";
import store from "./configureStore";ReactDOM.render(<Provider store={store}><App /></Provider>,document.getElementById("root")
);

localStorage.js代码

export const loadState = () => {try {const valueJSON = localStorage.getItem("state");return JSON.parse(valueJSON) || undefined;} catch (error) {return undefined;}
};
export const saveState = value => {const valueJSON = JSON.stringify(value);localStorage.setItem("state", valueJSON);
};

效果:
React-Redux总结含购物车案例
新增删除
React-Redux总结含购物车案例
完结~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/169746.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java实现ORM第一个api-FindAll

经过几天的业余开发&#xff0c;今天终于到ORM对业务api本身的实现了&#xff0c;首先实现第一个查询的api 老的C#定义如下 因为Java的泛型不纯&#xff0c;所以无法用只带泛型的方式实现api&#xff0c;对查询类的api做了调整&#xff0c;第一个参数要求传入实体对象 首先…

python中可变类型与不可变类型详细介绍

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 一.可变类型与不可变类型的特点 1.不可变数据类型 不可变数据类型在第一次声明赋值声明的时候, 会在内存中开辟一块空间, 用来存放这个变量被赋的值, 而这个变量实际上存储的, 并不是被赋予的这个值, 而是存放这个值所在空…

回归预测 | MATLAB实现BO-GRU贝叶斯优化门控循环单元多输入单输出回归预测

回归预测 | MATLAB实现BO-GRU贝叶斯优化门控循环单元多输入单输出回归预测 目录 回归预测 | MATLAB实现BO-GRU贝叶斯优化门控循环单元多输入单输出回归预测效果一览基本介绍模型搭建程序设计参考资料 效果一览 基本介绍 MATLAB实现BO-GRU贝叶斯优化门控循环单元回归预测。基于贝…

字节码进阶之JVM Attach API详解

字节码进阶之JVM Attach API详解 文章目录 字节码进阶之JVM Attach API详解附加到虚拟机加载代理和获取信息分离虚拟机 使用Attach API的基本步骤1. **获取虚拟机实例**&#xff1a;2. **附加到虚拟机**&#xff1a;3. **加载代理或获取信息**4. **从虚拟机分离**&#xff1a;…

C语言-面试题实现有序序列合并

要求&#xff1a; a.输入两个升序排列的序列&#xff0c;将两个序列合并为一个有序序列并输出。 数据范围&#xff1a; 1≤n,m≤1000 1≤n,m≤1000 &#xff0c; 序列中的值满足 0≤val≤30000 输入描述&#xff1a; 1.输入包含三行&#xff0c; 2.第一行包含两个正整数n, m&am…

python网络爬虫(二)基本库的使用urllib/requests

使用urllib 了解一下 urllib 库&#xff0c;它是 Python 内置的 HTTP 请求库&#xff0c;也就是说不需要额外安装即可使用。它包含如下 4 个模块。 request&#xff1a;它是最基本的 HTTP 请求模块&#xff0c;可以用来模拟发送请求。就像在浏览器里输入网址然后回车一样&…

html web前端,登录,post请求提交 json带参

html web前端&#xff0c;登录&#xff0c;post请求提交 json带参 3ca9855b3fd279fa17d46f01dc652030.jpg <!DOCTYPE html> <html><head><meta http-equiv"Content-Type" content"text/html; charsetutf-8" /><title></t…

rust学习——方法 Method

文章目录 方法 Method定义方法self、&self 和 &mut self方法名跟结构体字段名相同 带有多个参数的方法关联函数多个 impl 定义为枚举实现方法 rust 结构体与枚举的区别回答1回答2 方法 Method 从面向对象语言过来的同学对于方法肯定不陌生&#xff0c;class 里面就充斥…

Python-----for循环基本语法及其应用---对序列进行遍历循环

for循环基本语法 for循环结构主要用于&#xff08;序列 &#xff1a;包括 字符串、列表、元组、集合以及字典&#xff09;类型数据的遍历&#xff08;循环&#xff09;操作。 遍历(Traversal)&#xff0c;是指沿着某条搜索路线&#xff0c;依次对树&#xff08;或图&#…

nodejs+wasm+rust debug及性能分析

文章目录 背景v8引擎自带的profilelinux的perf采集wasm三方库性能分析编译debug版本wasmrust程序debug调试异常模型正常模型结论优化 参考 Node使用火焰图优化CPU爆涨 - 掘金 【Node.js丨主题周】理解perf 与火焰图-腾讯云开发者社区-腾讯云 Easy profiling for Node.js Applic…

RK3568平台开发系列讲解(应用篇)串口应用编程之串口的使用步骤

🚀返回专栏总目录 文章目录 一、配置参数1.1、获取当前串口的配置参数1.2、修改和写入串口的配置参数二、模式2.1、输入模式2.2、输出模式2.3、控制模式2.4、本地模式2.5、特殊控制字符沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 串口设备是嵌入式开发中最常用的…

Node.js在Python中的应用实例解析

随着互联网的发展&#xff0c;数据爬取成为了获取信息的重要手段。本文将以豆瓣网为案例&#xff0c;通过技术问答的方式&#xff0c;介绍如何使用Node.js在Python中实现数据爬取&#xff0c;并提供详细的实现代码过程。 Node.js是一个基于Chrome V8引擎的JavaScript运行时环境…

Flutter的Invalid use of a private type in a public API警告

文章目录 问题描述有问题的源码 问题原因解决方法 问题描述 自己在写Flutter 应用时发现了一个Invalid use of a private type in a public API警告。 发现很多官方的例子也有这个问题。 有问题的源码 有问题的源码如下&#xff1a; class MyTabPage extends StatefulWid…

【三维重建-PatchMatchNet复现笔记】

【三维重建-PatchMatchNet复现笔记】 1 突出贡献2 数据集描述3 训练PatchMatchNet3.1 输入参数3.2 制定数据集加载方式 1 突出贡献 在计算机GPU和运行时间受限的情况下&#xff0c;PatchMatchNet测试DTU数据集能以较低GPU内存和较低运行时间&#xff0c;整体误差位列中等&#…

python sqlalchemy(ORM)- 02 表关系

文章目录 表关系ORM表示 1v1ORM表示 1vm 表关系 1:1&#xff0c;表A 中的一条记录&#xff0c;仅对应表B中的一条记录&#xff1b;表B的一条记录&#xff0c;仅对应表A的一条记录。1:m&#xff0c;表A中的一条记录&#xff0c;对应表B中的多条记录&#xff0c;表B中的一条记录…

【机器学习】集成学习Boosting

文章目录 集成学习BoostingAdaBoost梯度提升树GBDTXGBoostxgboost库sklearn APIxgboost库xgboost应用 集成学习 集成学习&#xff08;ensemble learning&#xff09;的算法主要包括三大类&#xff1a;装袋法&#xff08;Bagging&#xff09;&#xff0c;提升法&#xff08;Boo…

在 Python 中使用 Pillow 进行图像处理【2/4】

第二部分 一、说明 该文是《在 Python 中使用 Pillow 进行图像处理》的第二部分&#xff0c;主要介绍pil库进行一般性处理&#xff1a;如&#xff1a;图像卷积、钝化、锐化、阈值分割。 二、在 Python 中使用 Pillow 进行图像处理 您已经学习了如何裁剪和旋转图像、调整图像大…

26. 通过 cilium pwru了解网络包的来龙去脉

pwru是一种基于eBPF的工具,可跟踪Linux内核中的网络数据包,并具有先进的过滤功能。它允许对内核状态进行细粒度检查,以便通过调试网络连接问题来解决传统工具(如iptables TRACE或tcpdump)难以解决甚至无法解决的问题。在本文中,我将介绍pwru如何在不必事先了解所有内容的…

【java】【重构二】分模块开发版本锁定以及耦合(打包)实战

目录 一、创建dependencyManagement标签 二、 将需要版本控制的依赖版本进行标签设置 三、将需要版本控制的依赖从各子模块迁移到此处 四、将父模块的依赖版本控制 五、删除子模块的全部版本 1、bocai-web-management模块 2、bocai-utils模块 六、打包 1、确定代码都…

shein面试:nacos无入侵配置,做过吗,怎么做?

说在前面 在40岁老架构师 尼恩的读者社区(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如阿里、滴滴、极兔、有赞、shein 希音、百度、网易的面试资格&#xff0c;遇到很多很重要的面试题&#xff1a; 无入侵配置&#xff0c;做过吗&#xff0c;怎么做的&#xff1f;Na…