React-Redux总结含购物车案例

news2024/11/21 1:33:45

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.data
      return [...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 }) => (
      <CartItem
        key={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.coloradmin.cn/o/1124027.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

内衣洗衣机有必要买吗?口碑好的小型洗衣机测评

在近年以来&#xff0c;由于人们对健康的认识和生活质量的不断改善&#xff0c;使得内衣洗衣机这一类的产品在近年来得到了飞速的发展&#xff0c;洗烘一体机、洗烘套装的价格总体下降&#xff0c;功能和性能都得到了改善&#xff0c;往往更多的用户会选择一台或者多台洗衣机来…

java--基本的算术运算符、+符号做连接符

运算符是对变量、字面量进行运算的符号 1.基本的算术运算符 注意&#xff1a;如果是整数相除&#xff0c;得到的还是整数&#xff0c;会舍去小数点后面的数的 取余最后得到的是两个数相除的到的余数 2.“”符号可以做连接符的 1.“”符号与字符串运算的时候是用作连接符的&am…

雷电模拟器端口号 adb连接

在尝试adb连接雷电模拟器时&#xff0c;网上查询了一下端口号&#xff0c;发现说是5555. 但是自己尝试&#xff0c;会提示&#xff1a; cannot connect to 127.0.0.1:5555: 由于目标计算机积极拒绝&#xff0c;无法连接。 (10061) 终于发现&#xff0c;因为我打开的模拟器&am…

观察者模式java

观察者模式是一种常见的设计模式&#xff0c;用于在对象之间建立一对多的依赖关系。在该模式中&#xff0c;一个主题&#xff08;被观察者&#xff09;维护了一个观察者列表&#xff0c;并在自身状态发生变化时通知所有观察者进行相应的更新。 观察者模式的核心概念包括以下几…

阿里企业邮箱域名解析MX记录表

阿里企业邮箱配置需要为域名添加MX解析记录&#xff0c;不只是MX域名解析记录值&#xff0c;还需要为域名添加pop3、imap、smtp及mail等CNAME解析类型&#xff0c;阿里云百科aliyunbaike.com分享阿里云企业邮箱域名MX解析记录类型、记录值及服务器地址&#xff1a; 新版阿里企…

自研操作系统到底有没有技术难度?

自研操作系统到底有没有技术难度? 最近小米自研操作系统的事情引起了大家热烈讨论。今天我们来从技术的角度来分析一下做自研操作系统到底有没有技术难度。先说结论: 大家放心,做自研操作系统没有中国公司不可跨越的技术难度。但是要把操作系统的性能&#xff0c;用户体验&a…

C++设计模式_11_builder 构建器(小模式,不太常用)

builder 构建器也是属于“对象创建模式”模式的一种&#xff0c;是一个不常用&#xff0c;比较小的模式。 文章目录 1. 动机&#xff08;Motivation&#xff09;2. 代码演示builder 构建器2.1 builder 构建器模式的形式1方法2.2 builder 构建器模式的形式2方法2.3 两种形式总结…

推荐一款最近风很大的配音工具~

逐渐发现身边越来越多人开始朝着丰富多彩的副业方向发展&#xff0c;而在这其中&#xff0c;“自媒体”深受大家喜爱&#xff0c;许多人开始看到了商机并纷纷“下海”制作短视频&#xff0c;那么不知道在座的各位“下海”了没有呢哈哈哈哈&#xff1f;短视频需求越来越大&#…

测试左移和右移怎么做,这篇文章写的太详细了

通俗的讲&#xff1a;左移是往开发阶段移&#xff0c;右移是往发布之后移。 正常测试&#xff1a;提测后的测试工作——到——发布验证完成阶段。 测试左移&#xff1a;提测之前的测试。 如&#xff1a;代码单元测试&#xff0c;代码质量检测&#xff0c;代码接口持续测试 等。…

为什么电商使用高匿代理ip更有效果?

前言 随着电商业务的不断发展&#xff0c;越来越多的电商平台开始使用代理IP作为一种有效的反爬虫手段。而高匿代理IP作为代理IP中的一种&#xff0c;其相较于其他类型的代理IP可以更好地保护电商业务的隐私和安全&#xff0c;从而更加有效地为电商平台服务。本文将从代理IP的…

苹果Mac电脑音频处理工具:iZotope RX 10 最新 for mac

iZotope RX 10是一款知名的音频修复和后期处理工具。它因其出色的音频修复功能和强大的音频编辑工具而被广泛应用于音乐制作、电影后期制作、广播电视等领域。 以下是iZotope RX 10的一些主要功能和特点&#xff1a; 音频修复&#xff1a;iZotope RX 10具备强大的音频修复功能…

北邮22级信通院数电:Verilog-FPGA(6)第六周实验:全加器

北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 先抄作业&#xff01;&#xff01;&#xff01;&am…

机器人系统 ROS 常用命令行工具

1. 启动ros 主节点 roscore roscore运行成功如图&#xff1a; 1.1 rosrun 启动服务节点 例子&#xff1a;启动一个小乌龟节点 rosrun turtlesim turtlesim_node运行结果如图&#xff1a; 1.2 启动键盘控制 打开新的命令窗口&#xff0c;启动turtle_teleop_key 节点 rosr…

三步搞定查分系统

成绩查询系统&#xff0c;简单来说&#xff0c;就是一个让学生能够自助查询成绩的系统。无论是在学校、培训机构&#xff0c;还是在家庭教育中&#xff0c;成绩查询都是一个重要的环节。那么如何建立一个简单实用的成绩查询系统呢&#xff1f;下面就为你揭秘三步打造查分系统的…

ubuntu1804服务器设置静态IP

修改/etc/netplan/00-install-config.yaml文件中的内容 查看网关命令是route -n

【广州华锐互动】关于物理力学的3D实验实操平台

在科学的广阔领域中&#xff0c;物理力学是一个至关重要的分支&#xff0c;它探索了物体在力作用下的运动规律。然而&#xff0c;传统的物理实验往往需要复杂的设备和大量的操作&#xff0c;这对于学生来说是一项巨大的挑战。为了解决这个问题&#xff0c;广州华锐互动开发了物…

GoLong的学习之路(二)语法之基本数据类型

书接上回&#xff1a;我在GoLong的学习之路&#xff08;一&#xff09;中在常量最后说了iota的作用。今天这里我在介绍一下我学习Go语言中基本数据类型。 文章目录 Go中的基本数据类型整型特殊整型数字字面语法 浮点型复数布尔值字符串字符串转义符多行字符字符串的常用操作&am…

小程序 swiper滑动

整个红色区域为可滑动区域&#xff0c;数字1区域为展示区域&#xff0c;数字2为下一个展示模块 <scroll-view class"h_scroll_horizontal" enhanced"ture" bind:touchend"touchEnd" bind:touchstart"touchStart"><view clas…

【iOS】——知乎日报第一周总结

文章目录 一、框架和布局问题二、线程冲突问题三、下拉刷新问题四、添加网络请求的图片五、时间标题设置问题 一、框架和布局问题 仿写知乎日报用到的框架依旧是MVC框架&#xff0c;所以一开始要想好该怎么搭建大体框架&#xff0c;对于各个模块该怎么分配&#xff0c;需要用到…

java--自增自减运算符

1.自增自减运算符 注意&#xff1a;、--只能操作变量&#xff0c;不能操作字面量的。 2.自增自减的使用注意事项 1.、--如果不是单独使用(如果在表达式中、或者同时有其它操作)&#xff0c;放在变量前后会存在明显区别 1.1放在变量前面&#xff0c;先对变量进行1、-1&#xff…