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
2 Answers
Reset to default 14To 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.
- It looks like you need to import
connect
fromreact-redux
- You need to destructure
addProduct
from your props now that you've mapped it withmapDispatchToProps
- I redefined
mapDispatchToProps
to not usebindActionCreators
, this is just a matter of preference, but either should work. - 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.