使用来自React-redux的连接时方法返回的状态未定义

时间:2018-10-17 03:11:13

标签: javascript reactjs redux react-redux

每当用户单击按钮时,我都试图调用handleClick方法,但是页面上的任何内容都不会呈现,并且出现错误“未捕获的ReferenceError:handleClick未定义”。

组件的实现:

import {createElement} from 'react';
import {add} from '../action/cart';
import {connect} from 'react-redux';
import styles from './styles.css';

handleClick = (id) => {
  add(id);
  this.setState((prevState) => ({
    ...prevState,
    items: prevState.items.map(
      (item) =>
        id === item.id
          ? {id, quantity: item.quantity + 1}
          : {...item}
    ),
  }));
};

const Product = ({add, id, title, image}) => (
  <div className={styles.product} onClick={handleClick(id)}>
    <img src={image} alt={title} className={styles.productImage}/>
    {title}
  </div>
);

export default connect(() => ({}), {add})(Product);

这与购物车组件共享状态:

const Cart = connect(
  () => ({}),
  {clear}
)(({items, clear, total}) => {
  return (
    <div>
      <Heading><FontAwesomeIcon icon={faShoppingCart} /> Cart</Heading>
      {items.length ? <button onClick={clear}>Clear all items</button> : null}
      <table>
        <thead>
          <tr>
            <th>Product</th>
            <th>Price</th>
            <th>Quantity</th>
            <th>Total</th>
          </tr>
        </thead>
        <tbody>
          {items.map(({...item}, id) => (
            <Item {...item} key={id} />
          ))}
        </tbody>
      </table>
      {items.length ?
        <div className={styles.total}>${total}</div>
        : <div>Your cart is empty!</div>}
    </div>);
});

export default connect((state) => {
  return {
    items: state.cart.items,
    total: reduce(
      (sum, {id, quantity}) => sum + products[id].price * quantity,
      0,
      state.cart.items
    ).toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,'),
  };
})(Cart);

它引用了此操作:

import {ADD_ITEM, SET_QUANTITY, CLEAR_ITEMS} from './types';
import {createAction} from 'redux-actions';

export const add = createAction(ADD_ITEM);
export const setQuantity = createAction(SET_QUANTITY);
export const clear = createAction(CLEAR_ITEMS);

使用此减速器

[ADD_ITEM]: (state, {payload: id}) => ({
    ...state,
    items: [
      ...state.items,
      {id, quantity: 1},
    ],
  }),

4 个答案:

答案 0 :(得分:1)

您正在为Product创建一个无状态组件,this不应在无状态组件内部使用(更不用说setState的处理程序了)。相反,您应该使Product成为常规组件,如下所示:

编辑(删除了先前的代码)

好的,我看到您已经更新了帖子中的代码。因此,以下几件事可能会让您绊倒:

如果还没有这样做,请摆脱setState中的handleClick。该逻辑应该在redux动作之内,因为您的所有状态似乎都在redux状态树中。

您为Cart呼叫了两次connect。删除第一个电话,其中:

const Cart = connect(
  () => ({}),
  {clear}
)(({items, clear, total}) => {

应成为:

const Cart = ({items, clear, total}) => {

我想你是这个意思...

<tbody>
  {items.map(({...item}, id) => (
    <Item {...item} key={id} />
  ))}
</tbody>

...就是这样(我假设products存在于您的代码库中,因为您在connect的{​​{1}}调用中使用了它):

Cart

我想你是这个意思:

<tbody>
  {items.map(({...item}, id) => (
    <Product {...products[id]} {...item} key={id} />
  ))}
</tbody>

要这样:

{
  total: reduce(
    (sum, {id, quantity}) => sum + products[id].price * quantity,
    0,
    state.cart.items
  ).toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,'),
}

然后,您需要从已删除的{ total: state.cart.items.reduce( (sum, {id, quantity}) => sum + products[id].price * quantity, 0, ).toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,'), } 调用中添加clear道具,回到剩下的道具中,剩下的就是:

connect

要回到先前删除的export default connect( state => ({ items: state.cart.items, total: state.cart.items.reduce( (sum, {id, quantity}) => sum + products[id].price * quantity, 0, ).toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,'), }), {clear}, )(Cart); setState的减速器可能类似于以下内容:

ADD_ITEM

我认为以上所有内容都应该使您接近所有工作。

编辑2

要回答您的评论,是因为您没有在减速器中处理CLEAR_ITEMS操作吗?也许您需要减速器看起来像这样?

[ADD_ITEM]: (state, {payload: id}) => {
  const itemAlreadyInCart = state.items.find(i => i.id === id);
  if (itemAlreadyInCart) {
    return {
      ...state,
      items: state.items.map(
        (item) =>
          id === item.id
            ? {id, quantity: item.quantity + 1}
            : {...item}
      ),
    }
  }
  else {
    return {
      ...state,
      items: [
        ...state.items,
        {id, quantity: 1, },
      ],
    }
  }
},

顺便说一句,我也注意到了另一个问题。我之前发布了此更改:

[ADD_ITEM]: (state, {payload: id}) => {
  const itemAlreadyInCart = state.items.find(i => i.id === id);
  if (itemAlreadyInCart) {
    return {
      ...state,
      items: state.items.map(
        (item) =>
          id === item.id
            ? {id, quantity: item.quantity + 1}
            : {...item}
      ),
    }
  }
  else {
    return {
      ...state,
      items: [
        ...state.items,
        {id, quantity: 1, },
      ],
    }
  }
},

[CLEAR_ITEMS]: (state) => {
  return {
    ...state,
    items: [],
  }
},

但是<tbody> {items.map(({...item}, id) => ( <Product {...products[id]} {...item} key={id} /> ))} </tbody> 中的id不是项的id键,而是map(({...item}, id)函数提供的数组的索引。您可能想要执行以下操作:

map

答案 1 :(得分:0)

似乎方法未绑定到事件。您可以使用进行显式尝试 .bind方法

答案 2 :(得分:0)

从示例中很难看出,但是如果要将方法从父组件传递到子组件,则需要对其进行绑定。这可以通过多种方式完成,但是直接开始的地方应该是onClick = {handleClick(id).bind(this)}

但是,在此示例中,尚不清楚您如何“共享状态”。如果您可以阐明应用程序的结构,那么我将阐明如何适当地绑定该方法。

答案 3 :(得分:0)

由于handleClick确实设置了setState,因此我建议您将事件处理函数移至类组件(也称为statefull组件),并将该函数作为prop传递给Product组件。由于产品组件是功能组件或无状态组件,因此不建议您更改状态。如果要在Product组件本身中处理handleClick,则将Product组件更改为statefull组件

handleClick也是一个箭头功能,因此您无需手动绑定。