【问题标题】:React / Redux: Actions automatically get called whenever page changeReact / Redux:每当页面更改时都会自动调用操作
【发布时间】:2019-07-29 11:42:14
【问题描述】:

我正在使用 Redux 制作一个简单的购物车应用程序。现在,每次我更改页面时,都会自动调用动作 3 次,这等于项目的数量。如果我去购物车页面,removeItems 操作会被调用 3 次,所以到目前为止我无法将商品添加到购物车。这可能是路由器的问题,但我无法发现问题。谁能解释我有什么问题?

Home.js

import React from 'react';
import { connect } from 'react-redux';
import { addToCart } from '../actions';

class Home extends React.Component {
    handleClick = id => {
        this.props.addToCart(id)
    }

    renderList = () => {
        return this.props.cart.slice(0, 3).map(item => {
                return (
                    <div className="card" key={item.id} style={{width: "200px", float: "left", marginRight: "20px"}}>
                        <div className="card-image">
                            <img src={item.imageUrl} alt={item.name} />
                            <span className="card-title">{item.name}</span>
                            <span to="/" 
                                className="btn-floating halfway-fab waves-effect waves-light red"
                                onClick={this.handleClick(item.id)}
                            >
                                <i className="material-icons">add</i>
                            </span>
                        </div>

                        <div className="card-content">
                            <p>{item.desc}</p>
                            <p><b>${item.price}</b></p>
                        </div>
                    </div>
                )
            })
        }

        render() {
            console.log(this.props.cart)

        return (
            <div className="container">
                <h3>Home</h3>
                <div className="box">
                    {this.renderList()}
                </div>
            </div>
        )
    }
}

const mapStateToProps = state => {
    return { cart: state.cart.items }
}

const mapStateToDispatch = dispatch => {
    return {
        addToCart: (id) => { dispatch(addToCart(id)) }
    }
}

export default connect(mapStateToProps, mapStateToDispatch)(Home);

Cart.js

import React from 'react';
import { connect } from 'react-redux';
import { removeItem } from '../actions';

class Cart extends React.Component {
    handleClick = (id) => {
        this.props.removeItem(id);
    }

    renderList = () => {
        if (this.props.addedItems.length !== 0) {
            return this.props.addedItems.map(item => {
                return (
                    <li className="collection-item avatar" key={item.id}>
                        <div className="item-img">
                            <img src={item.imageUrl} alt={item.name} style={{width: "120px"}} />
                        </div>

                        <div className="item-desc">
                            <span className="title">{item.name}</span>
                            <p>{item.content}</p>
                            <p><b>${item.price}</b></p> 
                        </div>
                        <button 
                            className="waves-effect waves-light btn pink remove"
                            onClick={this.handleClick(item.id)}    
                        >Remove</button>
                    </li>
                )
            })            
        }
        else {
            return <p>Nothing is in cart.</p>
        }
    }

    render() {
        return (
            <div className="container">
                <div className="cart">
                    <ul className="collection">
                    {this.renderList()}
                    </ul>
                </div>
            </div>
        )
    }
}

const mapStateToProps = state => {
    return { addedItems: state.cart.addedItems }
}

const mapDispatchToProps = dispatch => {
    return {
        removeItem: (id) => {dispatch(removeItem(id))}
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Cart);

Header.js

import React from 'react';
import { Link } from 'react-router-dom';

const Header = () => {
    return (
        <nav className="nav-wrapper">
            <div className="container">
            <Link to="/" className="brand-logo">Shopping</Link>

            <ul className="right">
                <li><Link to="/">Shop</Link></li>
                <li><Link to="/cart">Cart</Link></li>
                <li><Link to="/cart"><i className="material-icons">shopping_cart</i></Link></li>
            </ul>
            </div>
        </nav>
    )
}

export default Header;

减速器

import data from '../data.json';
import { ADD_TO_CART, REMOVE_FROM_CART } from "../actions/types";

const INITIAL_DATA = {
    items: data,
    addedItems: [],
    total: 0
}

const cartReducer = (state = INITIAL_DATA, action) => {
    switch(action.type) {
        case ADD_TO_CART:
            let addedItem = state.items.find(item => item.id === action.id);
            let existedItem = state.addedItems.find(item => action.id ===item.id);

            if (existedItem) {
                addedItem.quantity += 1;
                return {
                    ...state,
                    total: state.total + addedItem.price
                }                
            }
            else {
                addedItem.quantity = 1;
                let newTotal = state.total + addedItem.price;
                return {
                    ...state,
                    addedItems: [...state.addedItems, addedItem],
                    total: newTotal
                }
            }

        case REMOVE_FROM_CART:
            let itemToRemove = state.addedItems.find(item => action.id === item.id);
            let newItems = state.addedItems.filter(item => action.id !== item.id);

            let newTotal = state.total - itemToRemove.price;

            return {
                ...state,
                addedItems: newItems,
                total: newTotal
            }

        default: 
            return state;
    }
}

export default cartReducer;

操作

import { ADD_TO_CART, REMOVE_FROM_CART } from "./types";

export const addToCart = (id) => {
    return {
        type: ADD_TO_CART,
        id
    }
}

export const removeItem = (id) => {
    return {
        type: REMOVE_FROM_CART,
        id
    }
}

App.js

import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Header from './Header';
import Home from './Home';
import Cart from './Cart';

const App = () => {
    return (
        <BrowserRouter>
        <div className="app">
            <Header />

            <Switch>
                <Route exact path="/" component={Home} />
                <Route path="/cart" component={Cart} />
            </Switch>
            </div>
        </BrowserRouter>        
    )
}

export default App;

【问题讨论】:

    标签: reactjs redux react-redux react-router


    【解决方案1】:

    看起来你实际上是在渲染组件时调用你的点击处理程序,而不是仅仅传递处理程序函数,所以这就是动作被多次触发的原因。

    例如,在您的 Home.js 组件中更改以下代码:

    <span to="/" className="btn-floating halfway-fab waves-effect waves-light red" onClick={this.handleClick(item.id)} >
    

    到:

    <span to="/" className="btn-floating halfway-fab waves-effect waves-light red" onClick={() => { this.handleClick(item.id); }} >
    

    Cart.js 上也是一样的,改成:

    <button className="waves-effect waves-light btn pink remove" onClick={this.handleClick(item.id)}>Remove</button>
    

    到:

    <button className="waves-effect waves-light btn pink remove" onClick={() => {this.handleClick(item.id); }}>Remove</button>
    

    【讨论】:

    • 哇,它成功了!非常感谢! onClick={this.handleClick(item.id)} 和 onClick={() => { this.handleClick(item.id); 有什么区别? }?我知道我是通过编写第一个函数来调用函数,但是第二个函数是如何工作的?
    • 很高兴它有效!这两种形式的区别在于,在第一种形式中,您实际上是在渲染期间调用点击处理程序,而在第二种形式中,您正在定义一个函数来处理点击(但不调用它)。通常我们会像这样添加点击处理程序:onClick={myClickHandler},但是当您传递一些参数时,您实际上需要将处理程序调用包装在一个新的函数定义中,就像在onClick={() =&gt; { myClickHandler(myParameter); }} 中一样。 () =&gt; {} 是一个匿名函数定义,直到被点击才会被调用。
    • 所以如果有参数我写 onClick={() => { myClickHandler(myParameter); }} 如果没有,我可以删除 () 并只写函数名?
    • @kayak,没错!
    • 现在我明白了。谢谢你的解释:)
    猜你喜欢
    • 2020-02-21
    • 2018-12-09
    • 2019-09-30
    • 1970-01-01
    • 2019-04-26
    • 2017-09-12
    • 2018-10-28
    • 2016-09-21
    • 1970-01-01
    相关资源
    最近更新 更多