• React-Redux总结含购物车案例


    React-Redux总结含购物车案例

    reduc简介

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

    主要的四个特征:

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

    设计思想

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

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

    什么情况下使用redux

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

    redux工作流程

    redux工作流程
    图片不够清除太专业,看下方老王去借书
    redux工作流程

    component --> dispatch(action) --> reducer --> subscribe --> getState --> component
    
    • 1
    • 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:"载荷"}
    
    • 1

    特点:

    • 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
          ...
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    特点:

    • 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)
    
    • 1
    • 2

    特点:

    • 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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    cart/Cart.css代码

    .cartWrapper {
      display: grid;
      grid-template-columns: 150px 150px 100px 100px 200px;
      grid-gap: 10px;
      white-space: nowrap;
    }
    
    .total {
      text-decoration: underline;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    ProductList/product.css代码

    .productWrapper {
      display: grid;
      grid-template-columns: 150px 150px 300px;
      grid-gap: 10px;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    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]);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    constants.js代码

    export const ADD_PRODUCT = "ADD_PRODUCT";
    export const REMOVE_PRODUCT = "REMOVE_PRODUCT";
    
    • 1
    • 2

    数据源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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    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")
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

  • 相关阅读:
    算法训练day41|动态规划 part03(LeetCode343. 整数拆分、96.不同的二叉搜索树)
    vscode debug go
    SpringBoot启动流程简介
    Elasticsearch 聚合字段aggregate-metric-double
    淘宝API详情接口调用示例
    [MAUI 项目实战] 手势控制音乐播放器(四):圆形进度条
    java 中的常量
    【Linux】系列入门摘抄笔记-6-tar打包压缩和vim编辑器
    三十九、jQuery
    第十期|惊!游戏广告主投放十万被骗,推广作弊竟全是虚拟用户
  • 原文地址:https://blog.csdn.net/qq_42696432/article/details/133990318