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
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)}
/>
)
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
}
}
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 }