skip to Main Content

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

3

Answers


  1. 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.

    Login or Signup to reply.
  2. 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} />;
          })}
    
    Login or Signup to reply.
  3. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search