【Redux案例】实现购物车

1 效果说明

假设我在网购,有两个商品A、B,它们是独立的组件,当我想购买A商品时,我就进入A商品详情页将其添加到购物车,想购买B商品亦然。

初始页面:localhost:3000
在这里插入图片描述
点击A商品,进入A商品详情页面,localhost:3000/a
在这里插入图片描述
点击添加购物车,有相应的效果:
在这里插入图片描述
点击B商品,进入B商品详情页面,localhost:3000/b:
在这里插入图片描述
点击添加购物车,有如下效果:
在这里插入图片描述
点击删除可以删除任意商品:
在这里插入图片描述
在本案例中,购物车是一个共享组件,此时可以使用Redux对购物车实现统一管理。

2 实现代码

2.1 创建项目

使用WebStorm创建一个React项目,安装redux模块

npm install redux

项目的目录结构如下所示:
在这里插入图片描述

2.2 store.js

在该文件中主要通过createStore生成store,创建reducer函数,具体代码如下:

import {createStore} from "redux";

// 定义reducer函数,它有两个参数:state和action
// store是初始的状态,action是派发的动作,goods表示商品
const cartReducer = (state = {goods: []}, action) => {
    switch (action.type) {
        case "add": // 添加购物车
            // 将state中的所有属性遍历出来(只有goods)
            // goods数组中遍历出来之前的商品,再把新的商品加进去
            return {...state, goods: [...state.goods, action.good]};
        case "delete": // 从购物车中删除
            // 获取要删除的商品的索引
            let deleteGoodIndex = action.good.index;
            // 遍历所有的商品信息,并且放在新数组中
            let newGoods = [...state.goods];
            // 将要删除的商品从该数组中删除
            newGoods.splice(deleteGoodIndex, 1);
            return {...state, goods: newGoods}; // 删除商品之后的数组覆盖原数组
        default:
            return state;
    }
}

// 创建store对象
let store = createStore(cartReducer);

export default store; // 导出

2.3 A.js

在该文件中主要编写商品A的购物页面,具体代码如下:

import store from "./store";
import React from "react";

// 创建类组件
class A extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            // 设置当前的初始状态length为store的商品的数量
            length: store.getState().goods.length
        }
    }

    componentDidMount() { // 页面加载完成后执行该函数
        // 订阅store中state的变化,并返回一个可以取消订阅的对象
        this.unSubscribe = store.subscribe(() => { // state发生变化后,执行回调函数
            this.setState({
                length: store.getState().goods.length
            })
        })
    }

    componentWillUnmount() {
        this.unSubscribe(); // 组件卸载时取消订阅
    }

    render() {
        const {length} = this.state; // 将购买的商品的数量解析出来
        return (
            <div>
                <h2>A商品详情页面</h2>
                <p>当前购物车内商品数量:{length}</p>
                {/*点击按钮后派发add动作,将商品添加到购物车*/}
                <button onClick={() => store.dispatch({
                    type: "add",
                    good: {
                        title: `商品${Date.now()}`, // 将日期作为商品的编号
                        price: 100
                    }
                })}>添加购物车
                </button>
            </div>
        )
    }
}

export default A;

2.4 B.js

编写商品B的商品详情页面,与A.js相同,具体代码如下:

import store from "./store";
import React from "react";

class B extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            length: store.getState().goods.length
        }
    }

    componentDidMount() {
        this.unSubscribe = store.subscribe(() => {
            this.setState({
                length: store.getState().goods.length
            })
        })
    }

    componentWillUnmount() {
        this.unSubscribe();
    }

    render() {
        const {length} = this.state;
        return (
            <div>
                <h2>B商品详情页面</h2>
                <p>当前购物车内商品数量:{length}</p>
                <button onClick={() => store.dispatch({
                    type: "add",
                    good: {
                        title: `B商品${Date.now()}`,
                        price: 150
                    }
                })
                }>添加购物车
                </button>
            </div>
        )
    }
}

export default B;

2.5 Cart.js

购买商品时,我们不仅能看到商品数量在增加,同时我们也能看到自己买了哪个商品,以及将购买的商品删除,该文件主要实现这个功能,具体代码如下:

import store from "./store";
import React from "react";

class Cart extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            goods: store.getState().goods // 获取当前购买的商品列表
        }
    }

    componentDidMount() { // 页面加载完成后执行该函数
        this.unSubscribe = store.subscribe(() => {
            // 当state发生变化时执行回调函数
            this.setState({
                goods: store.getState().goods
            })
        })
    }

    componentWillUnmount() {
        this.unSubscribe(); // 取消订阅
    }

    render() {
        const {goods} = this.state; // 将商品列表获取出来
        return (
            <ul>
                {/*将商品遍历出来并显示在页面上*/}
                {
                    goods.map((good, index) => {
                        return (
                            <li key={index}>
                                {/*good.title是在A.js和B.js中定义的商品名称*/}
                                <span>{good.title}</span>
                                &nbsp;&nbsp;&nbsp;
                                <button onClick={() => store.dispatch({
                                    type: "delete",
                                    good: {index} // 将当前要删除的商品的索引派给good
                                })}>删除
                                </button>
                            </li>
                        )
                    })
                }
            </ul>
        )
    }
}

export default Cart;

2.6 App.js

在App.js中主要进行路由配置,代码如下:

import logo from './logo.svg';
import './App.css';
import {NavLink, Route} from "react-router-dom";
import B from "./components/B";
import A from "./components/A";
import Cart from "./components/Cart";


function App() {
    return (
        <div className="App">
            <header className="App-header">
                <img src={logo} className="App-logo" alt="logo"/>
                <div>
                    <NavLink to={"/a"}>A商品</NavLink>
                    &nbsp;&nbsp;&nbsp;
                    <NavLink to={"/b"}>B商品</NavLink>
                </div>
                <Route path={"/a"} component={A}/>
                <Route path={"/b"} component={B}/>
                <Cart/>
            </header>
        </div>
    );
}

export default App;

2.7 index.js

由于使用了Navlink,因此需要用BrowserRouter组件将App包起来。

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter} from "react-router-dom";

ReactDOM.render(
    <BrowserRouter>
        <App/>
    </BrowserRouter>,
    document.getElementById('root')
);
reportWebVitals();
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值