I'm a react newbie and im trying to build a simple shopping cart app without the use of class components after finding out about hooks. In my top level Component App I get a list of items via an imported json, then create a state with useState for the item list and also build a reducer to add or remove items from the cart which has its own state. Each state is being passed into a component, the ItemList together with a function to dispatch the cart state change (SpeiseListe in code) and the Cart. The ItemList maps the list of items to single Item Components, which have a button which onClick triggers said dispatch and the cart hook state in the top level component gets updated. Now the problem here is that the whole App rerenders, and so does the ItemList, on every action involving the cart. I would like to update only the Cart Component, but I'm not sure about the right approach.
(https://i.imgur.com/N6pfjvu.png)
I'm not sure what the solution or a concept to a solution could be. I was thinking of creating the states in ItemList (SpeiseListe) and Cart respectively, but then the Item Component would need to communicate with its parents sibling and that shouldnt be right?
App
import React, {useState, useReducer} from 'react'
import styled from 'styled-components'
import './bootstrap.min.css'
import Speisen from './Speisen'
import Cart from './Cart'
import * as speisen from './speisen.json'
export default function App() {
const cartHandlerReducer = (cart, action) => {
switch (action.type) {
case 'ADD_TO_CART':
return cart.concat(action.cartItem)
case 'REMOVE_FROM_CART':
return cart.filter(item => item.speise.id !== action.cartItem.speise.id)
default:
return cart
}
}
const speisenNumbered = speisen.default.speisen.map((item, i) => ({...item, itemOrder: i + 1}))
const [cart, dispatch] = useReducer(cartHandlerReducer, [])
const [speiseListe, setSpeiseListe] = useState(speisenNumbered)
const cartHandler = (cartItem) => {
if (cart.find(item => item.speise.id === cartItem.speise.id)) {
dispatch({type: 'REMOVE_FROM_CART', cartItem})
} else {
dispatch({type: 'ADD_TO_CART', cartItem})
}
}
const MainContainer = styled.div.attrs({
className: 'container'
})``
const Row = styled.div.attrs({
className: 'row'
})``
return (
<MainContainer>
<Row>
<Speisen
speisen={speiseListe}
cartHandler={cartHandler}
/>
<Cart
cart={cart}
/>
</Row>
</MainContainer>
)
}
Single Item Component
import React, {useState} from 'react'
import styled from 'styled-components'
function Button(props) {
return (
<button
onClick={props.onClick}
>
{props.added ?
'remove' :
'add'
}
</button>
)
}
const SpeiseContainer = styled.div.attrs({
className: 'row'
})`
margin-bottom: 60px;
`
const OrderCol = styled.div.attrs({
className: 'col-1'
})``
const NameCol = styled.div.attrs({
className: 'col'
})``
const PreisCol = styled.div.attrs({
className: 'col-2'
})`
text-align: right;
`
const ButtonCol = styled.div.attrs({
className: 'col-auto'
})``
export default function Speise (props) {
const {speise, accomps, cartHandler} = props
const [currentAccomp, setCurrentAccomp] = useState(accomps && accomps[0].id)
const changeCurrentAccomp = (event) => setCurrentAccomp(accomps[event.target.value].id)
const cartItemButton = () => {
cartHandler({speise, accomps, currentAccomp})
}
console.log(currentAccomp)
return (
<SpeiseContainer>
<OrderCol>
{speise.itemOrder}
</OrderCol>
<NameCol>
{speise.name}
{currentAccomp && (
accomps.length > 1 ? (
<select
onChange={changeCurrentAccomp}
>
{accomps.map((accomp, i) =>
<option
key={accomp.id}
value={i}
>
{accomp.name}
</option>
)}
</select>
):(
accomps[0].name
)
)}
</NameCol>
<PreisCol>
{speise.price}
</PreisCol>
<ButtonCol>
<Button onClick={cartItemButton} />
</ButtonCol>
</SpeiseContainer>
)
}
I would like to implement a state management using Hooks which makes it possible to add items to the shopping cart without rerendering the item list itself just because both states are being managed in the same component or a different concept of how to do this at all.
Thanks a lot!