bp123
bp123

Reputation: 3417

Child component doesn't update when parent updates

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. addQuantityButtonfunction 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

Answers (1)

buzatto
buzatto

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

Related Questions