最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to add action to functional component using Redux and ReactJS - Stack Overflow

programmeradmin0浏览0评论

I try to add the action "addProduct" to the component. But I see an error when I click on it, help me cope, thanks in advance!

I don't understand where is my mistake(

import { bindActionCreators } from "redux";
import { addProduct } from "../actions/addProduct";

const ProductListItem = ({ product }) => {
  return (
    <div className="product">
      <img className="product_img" src={product.image} />
      <p>{product.name}</p>
      <p className="bold">$ {product.price}</p>
      <button className="add_card" onClick={() => addProduct(product)}>
        {" "}
        add to cart
      </button>
    </div>
  );
};

export default ({ products = [] }) =>
  products.map((product, i) => {
    return <ProductListItem key={i} product={product} />;
  });


const mapDispatchToProps = dispatch => {
  return bindActionCreators({ addProduct }, dispatch);
};

connect(null,
  mapDispatchToProps
)(ProductListItem);
[enter image description here][1]

I try to add the action "addProduct" to the component. But I see an error when I click on it, help me cope, thanks in advance!

I don't understand where is my mistake(

import { bindActionCreators } from "redux";
import { addProduct } from "../actions/addProduct";

const ProductListItem = ({ product }) => {
  return (
    <div className="product">
      <img className="product_img" src={product.image} />
      <p>{product.name}</p>
      <p className="bold">$ {product.price}</p>
      <button className="add_card" onClick={() => addProduct(product)}>
        {" "}
        add to cart
      </button>
    </div>
  );
};

export default ({ products = [] }) =>
  products.map((product, i) => {
    return <ProductListItem key={i} product={product} />;
  });


const mapDispatchToProps = dispatch => {
  return bindActionCreators({ addProduct }, dispatch);
};

connect(null,
  mapDispatchToProps
)(ProductListItem);
[enter image description here][1]
Share Improve this question edited Jun 2, 2019 at 21:13 Anon asked Jun 2, 2019 at 20:20 AnonAnon 3071 gold badge5 silver badges17 bronze badges 2
  • 1 first argument of connect should be a function. usually it is called mapStateToProps which returns data from your state and makes it available inside component props – Hayk Aghabekyan Commented Jun 2, 2019 at 21:05
  • @Anon, any luck on resolving the issue? :o – Cat_Enthusiast Commented Jun 2, 2019 at 23:02
Add a comment  | 

2 Answers 2

Reset to default 14

To properly add your action, you need to map dispatch to you component, using redux connect function.

function connect(mapStateToProps?, mapDispatchToProps?, ...)

The first parameter is used to map state to props.

const mapStateToProps = state => {
    return { products: state.products, cart: state.cart };
};

const Item = connect(mapStateToProps)(MyItemComp)

Now in your component you have access to products and cart as props. Which you can access using this.props.products

The second parameter is used to allow your component to dispatch actions.

function mapDispatchToProps(dispatch) {
  return {
    addProduct: e => dispatch(addProduct(e))
  };
}

const Item = connect(mapStateToProps, mapDispatchToProps)(MyItemComp)

Now we can dispatch actions from our component, by executing addProduct(product).

connect can be used like

// will only access props, won't dispatch any actions
const Item = connect(mapStateToProps)(MyItemComp)
// dispatch actions, won't access props
const Item = connect(null, mapDispatchToProps)(MyItemComp)
// will access props and dispatch actions
const Item = connect(mapStateToProps, mapDispatchToProps)(MyItemComp)

Now let's fix your component

 import { addProduct } from "../actions/addProduct";
 import { connect } from "react-redux";

// add addProduct as a second parameter, to be able to dispatch actions from here
const ProductListItem = ({ product, addProduct }) => {
  return (
    <div className="product">
      <img className="product_img" src={product.image} />
      <p>{product.name}</p>
      <p className="bold">$ {product.price}</p>
      <button className="add_card" onClick={() => addProduct(product)}>
       {" "}
       add to cart
      </button>
    </div>
  );
};

Change

export default ({ products = [] }) =>
  products.map((product, i) => {
  return <ProductListItem key={i} product={product} />;
});

to

// export ProductListItem
export default ({ products = [], addProduct }) =>
  products.map((product, i) => {
    return <ProductListItem key={i} product={product} addProduct={addProduct}/>;
});

Now we need to attach props and dispatcher

// import the above export
import ProductListItem from './ProductListItem';

const ProductList = connect(
  mapStateToProps,
  mapDispatchToProps
)(ProductListItem);

In your parent component

render() {
  ...
  return(
    <div>
      ...
      <ProductList />
      ...
    </div>
   )
}

DEMO

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.1/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.0.3/react-redux.min.js"></script>
<script src="http://wzrd.in/standalone/uuid%2Fv1@latest"></script>
<link
      href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
      rel="stylesheet"
      integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN"
      crossorigin="anonymous"
    />
<style>
  .product {
     display: flex;
     flex-direction: row;
     border-bottom-style: solid;
     margin-bottom: 5px;
     margin-top: 10px;
   }

   img.product_img {
     width: 30px;
   }

   .badge {
     padding-left: 9px;
     padding-right: 9px;
     -webkit-border-radius: 9px;
     -moz-border-radius: 9px;
     border-radius: 9px;
    }

    .label-warning[href],
    .badge-warning[href] {
      background-color: #c67605;
    }
    #lblCartCount {
      font-size: 12px;
      background: #ff0000;
      color: #fff;
      padding: 0 5px;
      vertical-align: top;
      margin-left: 1px;
    }
 </style>
    
 <div id="root"></div>
    
<script type="text/babel">
const { Provider, connect } = ReactRedux;
const { applyMiddleware, createStore, combineReducers } = Redux;

function addProduct(payload) {
  return { type: 'ADD_PRODUCT', payload };
}

const initialState = {
  products: [
    {
      id: 1,
      name: 'Avocado',
      price: 1.5,
      image: 'https://img.icons8.com/metro/26/000000/avocado.png',
      quantity: 0
    },
    {
      id: 6,
      name: 'Bread',
      price: 1,
      image: 'https://img.icons8.com/metro/26/000000/bread.png',
      quantity: 0
    },
    {
      id: 2,
      name: 'Milk',
      price: 1.8,
      image: 'https://img.icons8.com/metro/26/000000/milk-bottle.png',
      quantity: 0
    }
  ],
  cart: []
};

function rootReducer(state = initialState, action) {
  if (action.type == 'ADD_PRODUCT') {
    return {
      ...state,
      cart: [...state.cart, action.payload]
    };
  }
  return state;
}

const store = createStore(rootReducer);

const mapStateToProps = state => {
  return { products: state.products, cart: state.cart };
};

function mapDispatchToProps(dispatch) {
  return {
    addProduct: e => dispatch(addProduct(e))
  };
}

const CartItems = ({ cart }) => {
  return (
    <div>
      <i class="fa fa-shopping-cart" />
      <span class="badge badge-warning" id="lblCartCount">
        {cart.length}
      </span>
    </div>
  );
};

const ProductListItem = ({ product, addProduct }) => {
  return (
    <div className="product">
      <img className="product_img" src={product.image} />
      <p>{product.name}</p>
      <p style={{ marginLeft: 5, marginRight: 5 }} className="bold">${product.price}</p>
      <button className="add_card" onClick={() => addProduct(product)}>
        {' '}
        add to cart
      </button>
    </div>
  );
};

const Cart = connect(mapStateToProps)(CartItems);
const Products = ({ products = [], addProduct }) =>
  products.map((product, i) => {
    return <ProductListItem key={i} product={product} addProduct={addProduct}/>;
  });

const ProductList = connect(
  mapStateToProps,
  mapDispatchToProps
)(Products);

class App extends React.Component {
  render() {
    return (
      <div>
        <Cart />
        <ProductList />
      </div>
    );
  }
}
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
</script>

I made a few updates and included an example of what i'm doing.

  1. It looks like you need to import connect from react-redux
  2. You need to destructure addProduct from your props now that you've mapped it with mapDispatchToProps
  3. I redefined mapDispatchToProps to not use bindActionCreators, this is just a matter of preference, but either should work.
  4. In your connect(), I destructure all remaining props by using ...rest and spread that into the returned as props.

..

Parent

import React from "react"
import ProductListItem from "./ProductListItem"

const products = [1,2,3] <-- data might be in redux-state but I'll use this for simplicity.

const TestHolder = () => {
    return(
        <div>
            <ProductListItem products={products}/>
        </div>
    )
}

export default TestHolder

ProductListItem

import React from "react"
import { connect } from "react-redux"
import { addProduct } from "../../actions/testActions";

const ProductListItem = ({ product, addProduct }) => {
  return (
    <div className="product">
      <button className="add_card" onClick={() => addProduct()}>
        {" "}
        add to cart
      </button>
    </div>
  );
};

const mapDispatchToProps = dispatch => {
  return {
    addProduct: (product) => {
      dispatch(addProduct(product))
    }
  }
};

export default connect(null, mapDispatchToProps)(({ products = [], ...rest }) =>
  products.map((product, i) => {
    return <ProductListItem key={i} product={product} {...rest} />;
  }))

Action creator

export const addProduct = () => {
    return {
        type: "Woof"
    }
}

You might have malformed your action-creator and made it a plain-object instead of a function that returns an object.

发布评论

评论列表(0)

  1. 暂无评论