0

Nearly done with a side project, last thing to fix is a button not working.
Was mostly referring to this article for guidance, yet something went wrong.

The Store part looks like this (there's a database call going on in parallel, hence the thunk, in case you're wondering):

const initState = {
    ...
    total: 100
}

//actions
...
export const addToCart = sum => ({
    type: 'ADD_CART',
    payload: sum
})

//reducers
const reduceProducts = (state = initState, action) => {
    switch(action.type){
        ...
        case 'ADD_CART':
            return {
                total: state.total + action.payload
            };
        default:
            return state;
    }
}

...

//store
const store = createStore(
    reduceProducts, 
    applyMiddleware(thunk)
);

export default store;

The component in question:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import store, { addToCart } from '../store/store.js';
import { Navbar, Nav, NavItem, CardColumns, Card, Button } from 'react-bootstrap';
import { LinkContainer } from 'react-router-bootstrap';


//Navbar component
class App extends Component {

    ...

    render() {
        if (!this.props.products.length){
            return <div> Loading... </div>
        }
        return (
            ...
                    <Navbar.Collapse>
                        <Nav> 
                            <LinkContainer to="/cart">
                                <NavItem>{this.props.total}</NavItem>
                            </LinkContainer>
                            ...
                        </Nav>
                    </Navbar.Collapse>
                </Navbar>
                    <CardColumns>
                    {
                        this.props.products.map( item => { return <PizzaTable pizzas={item} key={item.id}/> }) 
                    }       
                    </CardColumns>
            </div>
        );
    }
}


//Cart component
class PizzaTable extends Component{
    handleCart = (e) =>{
        addToCart(e.target.id);
    }

    render(){
        return(
            <Card>
                ...
                <Button id={this.props.pizzas.price} onClick={this.handleCart}>{this.props.pizzas.price}</Button>
            </Card>
        );
    }
}

const mapStateToProps = state => ({
    ...
    total: state.total
})

export default connect(mapStateToProps, {addToCart})(App);

Tried to keep all the unrelated code to a minimum, the database fetch and render work just fine.

addToCart function is available in the global scope, so using it directly, without passing as a prop or binding. console with all the props being fetched

How this should work:

  • Button's clicked, handleCart is activated.
  • It takes the e.target.id (a certainly numeric value, console.logged) and passes it to addToCart.
  • addToCart then uses the shorthand for mapDispatchToProps, calls the action, with e.target.id being the payload.
  • Reducer adds e.target.id to total and updates the view in NavItem.

Yet somewhere in the region of mapDispatchToProps, the value being passed is getting lost - logging the addToCart payload inside the store doesn't show anything. Either I used dispatch incorrectly, or forgot to call or return something obvious.

  • 1
    Doesn't look like `PizzaTable` is connected to your redux store. `App`, however, is, but doesn't have a button with onClick handler to "addToCart". – Drew Reese Jun 25 '20 at 06:52
  • 1
    The `{addToCart}` in your connect shouldn't be necessary, instead try `this.props.dispatch(addToCart(e.target.id))` – po.pe Jun 25 '20 at 06:53

1 Answers1

1

PizzaTable isn't connected to your redux store. Looks like you instead connected its addToCart action creator to App by mistake.

//Cart component
class PizzaTable extends Component{
  handleCart = (e) =>{
    addToCart(e.target.id);
  }

  render(){
    return(
      <Card>
        ...
        <Button
          id={this.props.pizzas.price}
          onClick={this.handleCart}
        >
          {this.props.pizzas.price}
        </Button>
      </Card>
    );
  }
}

const ConnectedPizzaTable = connect(null, { addToCart })(PizzaTable);

And in App, use the connected pizza table component, and remove addToCart from its connect HOC.

<CardColumns>
  {
    this.props.products.map(item => (
      <ConnectedPizzaTable pizzas={item} key={item.id}/>
    ) 
  }       
</CardColumns>

...

export default connect(mapStateToProps)(App);
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Not by mistake, App is the parent component that receives all the props and the incoming database info and then, supposedly, passes it to the child PizzaTable. So I would have to connect each and every child component to the store... got it, thank you! – alittlebyte Jun 25 '20 at 07:07
  • 1
    @VladFedorov yeah, the alternative is "prop drilling" which is literally the problem react-redux, and by implementation, react context API, solves. – Drew Reese Jun 25 '20 at 07:10