1

I am trying to have a user be able to click an item from a list of all possible items and have a modal open to display data about that item (including the current quantity they have) and buttons to increment/decrement that amount.

To my understanding since I am just showing data that is being passed in and then dispatching an action to update the store I should be using a functional component to display the data and useDispatch to call the store action.

Currently when I update the store I see the change in Redux debugging tools but the change is not reflected in the modal until I reopen it. While I have been looking for answers to this I see many similar questions but they all use Class Components and mapStateToProps (such as this post). I thought best practices was to use functional components unless needed. Am I wrong to think that if I am getting a value from the store in a functional component it should update on change?

Code Snippets

  • Dialog
export default function ItemDialog({
    ...
    selectedItem,
}) {
    const dispatch = useDispatch()
    const inventory = useSelector(
        state => state.user.inventory
    )
    let userItem = inventory.find(
        userItem => userItem.name === selectedItem.name
    )

    const changeItemCount = (item, change) => {
        item.change = change
        dispatch({
            type: "USER_INVENTORY_UPDATED",
            payload: item
        })
    }

    const showQuantity = userItem => {
        return userItem.quantity > 0 ? `(${userItem.quantity})` : ""
    }
...

render(
    <p className="text-xl text-center font-semibold">
        {selectedItem.name}
    </p>
    <p className="text-center font-light">
        {showQuantity(userItem)}
    </p>

    ...
    <AddBoxIcon
        onClick={() => changeItemCount(selectedItem, 1)}
    />
)
  • Store
const userReducer = (state = InitialUserState, action) => {
    let inventoryCopy = { ...state.inventory }

    switch (action.type) {
        case "USER_INVENTORY_UPDATED":
            let category = action.payload.category
            let updatedItemIndex = inventoryCopy[category].findIndex(
                item => item.name === action.payload.name.toUpperCase()
            )

            // If item is already there
            if (updatedItemIndex >= 0) {
                inventoryCopy[category][updatedItemIndex].quantity +=
                    action.payload.change
            } else {
                // If item needs to be added to inventory category
                let newItem = {
                    name: action.payload.name,
                    quantity: action.payload.change
                }
                inventoryCopy[category].push(newItem)
            }

            return {
                ...state,
                inventory: inventoryCopy
            }

            ...

            default:
            return state
    }
}

av0000
  • 1,917
  • 6
  • 31
  • 51
  • 1
    You can have a stateless functional component with an action creator passed into it from a container; it doesn't need to be a class. – Chad Feb 07 '20 at 17:27
  • 1
    So your count is not being updated or whole data is not being shown? – Zohaib Ijaz Feb 07 '20 at 17:28
  • 1
    Can you post your store state? The answer I gave is vague because I don't know what the state object looks like. – Chad Feb 07 '20 at 17:33

1 Answers1

1

Check your spread operator when you return your updated state. You may need to deep clone the old state depending on how many nested objects it has.

The docs have more information on shallow cloning objects.

Deeply cloning your state object will help you get rid of:

let inventoryCopy = { ...state.inventory }

Chad
  • 608
  • 5
  • 15
  • Store has a User reducer that is an object with different categories as keys (like clothes) inside each category there might be subcategories. It would make sense that maybe cloning is the issue. – av0000 Feb 07 '20 at 17:41
  • 1
    I just updated my action to create a copy at each level of my store and then updated the relevant level, fed that back into the higher levels of the store, and that fixed it. Thank so much! – av0000 Feb 07 '20 at 17:51
  • Yes, you were mutating your state. I'd encourage you to start using our new [Redux Toolkit package](https://redux-toolkit.js.org), which would have thrown errors when the mutations were detected, and also allows you to write much simpler reducer logic. – markerikson Feb 08 '20 at 06:37