0

so I am declaring a usestate variable as "count" and I am mapping through a list of 2 items. This code shows the variable declaration and the function to increment and decrement

const [count, setCount] = useState(1); const increment = () => { setCount((prev) => prev + 1); }; const decrement = () => { setCount((prev) => { if (prev == 1) return prev; return prev - 1; }); }; 

And the next code shows how I am mapping through an item and displaying a text that holds the count variable

{ cartItemArr.map((entry: any, index: number) => { return ( <Flex key={index} w={'100%'} alignItems={'center'} justifyContent={'space-between'} > <Flex direction={'column'} justifyContent={'space-between'}> <Text fontWeight={600} fontSize={'14px'} textColor={'#000'}> {entry?.foodId.nameOfFood} </Text> <Text fontWeight={400} fontSize={'12px'} textColor={'#000'}> #{entry?.foodId.pricePerServing} </Text> </Flex> <HStack justify={'center'} w="109px" borderRadius={'40px'} py="8px" border={'1px solid #AA9F93'} > <AiOutlineMinus cursor={'pointer'} onClick={decrement} className='cursor-pointer' /> <Text>{count}</Text> <AiOutlinePlus cursor={'pointer'} onClick={increment} className='cursor-pointer' /> </HStack> </Flex> ) }) } 

So when I click on the first item, the second item also increases. I know that it is because both of them have the count variable which is why it is behaving like that, is there something I can do that will make the counts not affect each other on the mapped items? I hope I can communicate my issue well. For better context, this is the screen I am building; enter image description here

2
  • Why not store a quantity property on each cartItemArr entry? Commented Dec 27, 2023 at 22:10
  • 1
    index makes for a poor key. Do your cart items have some other unique identifier you could use? Commented Dec 27, 2023 at 22:14

3 Answers 3

1

Create a separate React component for the individual entries, something like this:

const Entry = ({ entry }: { entry: any }) => { const [count, setCount] = useState(1); const increment = () => { setCount((prev) => prev + 1); }; const decrement = () => { setCount((prev) => { if (prev == 1) return prev; return prev - 1; }); }; return ( <Flex w="100%" alignItems="center" justifyContent="space-between" > <Flex direction="column" justifyContent="space-between"> <Text fontWeight={600} fontSize="14px" textColor="#000"> {entry?.foodId.nameOfFood} </Text> <Text fontWeight={400} fontSize="12px" textColor="#000"> #{entry?.foodId.pricePerServing} </Text> </Flex> <HStack justify="center" w="109px" borderRadius="40px" py="8px" border="1px solid #AA9F93" > <AiOutlineMinus cursor="pointer" onClick={decrement} className="cursor-pointer" /> <Text>{count}</Text> <AiOutlinePlus cursor="pointer" onClick={increment} className="cursor-pointer" /> </HStack> </Flex> ); }; 

That way the count variable is local to each entry rather than to the entire cart.

Then update your map to something like this:

 {cartItemArr.map((entry: any, index: number) => { return <Entry entry={entry} key={index} />; })} 
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for taking the time to answer. Your solution solved the issue, I appreciate
If you don't mind, can you drop me a way I can reach you if I run into problems? Maybe you'd be able to put me through on some things.
@OlaoluwaAnigboro-Napoleon It's preferable to ask new questions on SO so others can benefit from the answers as well. If it's an extension of this current question, feel free to make a stackoverflow chat and comment the link here. chat.stackoverflow.com/rooms/new
Okay, my concern was that I might run into an issue that I may not be able to accurately present here, that's why. But I will try my best to always ask my question on SO. Thank you!
1

You are using the same variable count for all entries. So, when you change in one place, it will change in all other places because the variable is the same.

Try to use a list (an object indexed by foodId, for example) of counts, like this:

// this reduce will map all foodIds and set them as count = 1 const [count, setCount] = useState(cartItemArr.reduce((a, {entry: e}) => ({ ...a, [e.foodId]: 1}), {})); // try console.log(count) if is not clear yet const increment = (foodId) => { () => { setCount((prev) => {...prev, [foodId]: prev[foodId] + 1}); } }; const decrement = (foodId) => { // now you need to return a function with an fixed `foodId` in scope for each one () => { setCount((prev) => { if (prev[foodId] == 1) return prev; return {...prev, [foodId]: prev[foodId] - 1}; }); } }; 

and in you jsx:

<AiOutlineMinus cursor={'pointer'} onClick={decrement(entry.foodId)} className='cursor-pointer' /> <Text>{count}</Text> <AiOutlinePlus cursor={'pointer'} onClick={increment(entry.foodId)} className='cursor-pointer' /> 

This should make it work as expected.

Note that there are some better ways to do it, like break into smaller components. As you learn more React it will probably become clearer to you. So, keep improving! :)

But for now, i hope this answer can help you unstuck and make progress on your work.

Comments

-2

Recognize that when, in JSX, you do:

cartItemArr.map((entry: any, index: number) => { [[SOME_CODE_HERE]] }); 

the [[SOME_CODE_HERE]] is actually a React component. If you want to track state for each iteration of that .map(), then consider putting state (i.e. use useState()) inside [[SOME_STUFF_HERE]].

And...to be a better/cleaner codebase, move the definition of [[SOME_CODE_HERE]] to its own file (e.g. SomeCodeHere.jsx) so that it is clear that this is a component, and that it be maintained as such.

4 Comments

The second paragraph is completely incorrect - SOME_CODE_HERE is NOT a react component and you can't just put a useState inside the map function like that unless you can guarantee that cartItemArr won't change. The answer is also missing the detail needed to be understandable to someone who is clearly very new to react.
Totally incorrect. Of course SOME_CODE_HERE is a react component. It is a function that returns JSX to be rendered. How else do you define a component? And if they are "clearly new to React" then there is no such indication in the post or by SO itself.
Here's a playground demonstrating why you cannot do this: codesandbox.io/p/sandbox/reactjs-playground-forked-4697yr . As soon as you click "Add Item", you get "Invariant Violation - Rendered more hooks than during the previous render". This is because all the cnt hooks are treated as being part of App, not an independent inline component.
Ah, right...my bad. I don't allow myself to code components inline like that, so I've forgotten that you actually need to define a proper component rather than simply inject JSX inline. Here's what I was thinking: codesandbox.io/p/sandbox/… Apologies @DanielCentore for my bluntness. I was wrong.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.