Reputation: 3417
When I add a new product to the products array using the addQuantityButton
function via the child component, the change to the products array isn't recognised in the child Product component. addQuantityButton
function in the parent component is correctly adding a new object to the array. This stopped working when I started using Redux and mapStateToProps. What am I doing wrong here?
Path: Parent
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
products: [getNewProduct()]
};
this.addQuantityButton = this.addQuantityButton.bind(this);
}
addQuantityButton(product_id) {
let { products } = this.state;
products = products.map((product) => {
if (product.product_id === product_id) {
product.quantities.push(getNewQuantity());
return product;
}
return product;
});
this.setState({
products
});
}
render() {
const { products } = this.state;
return (
<form>
{products.map((product) => (
<Product
key={product.key}
product_id={product.product_id}
quantities={product.quantities}
addQuantityButton={this.addQuantityButton}
/>
))}
</form>
);
}
}
Path: Product
class Product extends React.Component {
constructor(props) {
super(props);
this.state = {
unitOptions: []
};
}
render() {
return (
<div>
<div>
{this.props.quantities.map((quantity) => (
<p>Example</p>
))}
</div>
<button
type="button"
onClick={() => addQuantityButton(this.props.product_id)}
>
Add quanity
</button>
</div>
);
}
}
Product.propTypes = {
product_id: PropTypes.string,
quantities: PropTypes.array,
addQuantityButton: PropTypes.func
};
const mapStateToProps = (state) => ({
supplierProducts: state.product.products
});
export default connect(mapStateToProps)(Product);
Upvotes: 1
Views: 56
Reputation: 10382
child is listening to redux's state, which is different from parent's state. react component state is one thing, redux state is other. copying redux's state to a component's state is not advisable, you are duplicating state.
I would suggest at Parent's to map only your products to props, then iterate at your form as this.props.products.map(...
while at Children you declare a mapDispatchToProps
responsible to increment the quantity. there you declare your addQuantityButton
with some refactors. you will use dispatch instead which receives an action. the logic to add product will be implemented at your reducer down the road.
const mapDispatchToProps = (dispatch, ownProps) => ({
addQuantityButton: dispatch(addQuantity(ownProps.product_id))
})
your action is a simple function declared at some actions file, that return an object containing the action TYPE and a payload (you could call the payload something else fwiw):
const addQuantity = product_id => ({
type: 'ADD_QUANTITY',
payload: product_id
})
now, dispatch with a proper action will pass down the object to reducers, and a given reducer that intercepts ADD_QUANTITY
will be responsible to increment quantity, and that way return next redux state.
at reducer you implement the logic to update state.
function productsReducer(state = initialState, action) {
switch (action.type) {
case 'ADD_QUANTITY': // suggestion, declare your types as a constant in another file
// also, dont mutate state directly!, you may need to use some deep clone given it's an array of objects
return // logic here with action.payload and state.products
default:
return state
}
}
Upvotes: 2