Next.js Discord

Discord Forum

Pushing data pushes twice to array

Answered
German yellowjacket posted this in #help-forum
Open in Discord
Avatar
German yellowjacketOP
I have this data structure
let sampleWorkoutData: WorkoutData = {
   workoutName: 'Chest Day',
   notes: 'This is a sample workout for chest day',
   exercises: [
      {
         exerciseName: 'Dumbbell Flys',
         sets: [
            {
               setNumber: 1,
               reps: 10,
               weight: 20
            },
            {
               setNumber: 2,
               reps: 8,
               weight: 25
            },
            {
               setNumber: 3,
               reps: 6,
               weight: 30
            }
         ]
      }
   ]
}


and i when i press this button
<Button variant="link" onClick={() => addSet(exerciseIndex)}>Add Set</Button>


it runs this
const [exercises, setExercises] = useState(workoutData.exercises);
   const isAddingSet = useRef(false);

   const addSet = (exerciseIndex) => {
      if (isAddingSet.current) return;
      isAddingSet.current = true;

      setExercises(prevExercises => {
         const updatedExercises = [...prevExercises];
         updatedExercises[exerciseIndex].sets.push({
            setNumber: updatedExercises[exerciseIndex].sets.length + 1,
            reps: 0,
            weight: 0,
         });
         return updatedExercises;
      });

      setTimeout(() => {
         isAddingSet.current = false;
      });
   };

but it appends 2 sets to the exercise instead of one. Not sure

6 Replies

Avatar
German yellowjacketOP
the full code is here
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Separator } from '@/components/ui/separator';

import React, { useState, useRef } from 'react';

function WorkoutDetails({ workoutData }) {
   const [exercises, setExercises] = useState(workoutData.exercises);
   const isAddingSet = useRef(false);

   const addSet = (exerciseIndex) => {
      if (isAddingSet.current) return;
      isAddingSet.current = true;

      setExercises(prevExercises => {
         const updatedExercises = [...prevExercises];
         updatedExercises[exerciseIndex].sets.push({
            setNumber: updatedExercises[exerciseIndex].sets.length + 1,
            reps: 0,
            weight: 0,
         });
         return updatedExercises;
      });

      setTimeout(() => {
         isAddingSet.current = false;
      });
   };

   return (
      <div>
         <h2>Workout: {workoutData.workoutName}</h2>
         <p>Notes: {workoutData.notes}</p>

         <div>
            {exercises.map((exercise, exerciseIndex) => (
               <div key={exerciseIndex}>

                  <h3>Exercise: {exercise.exerciseName}</h3>
                  <ul>
                     {exercise.sets.map((set, setIndex) => (
                        <li key={setIndex}>
                           Set {set.setNumber}: {set.reps} reps, {set.weight} lbs
                        </li>
                     ))}
                  </ul>
                  <Separator />
                  <Button variant="link" onClick={() => addSet(exerciseIndex)}>Add Set</Button>

               </div>
            ))}
         </div>

      </div>
   );
}

export default WorkoutDetails;
Answer
Avatar
this comment should explain what is happening. You are in dev, so mostly its the strict mode coz the code looks correct otherwise
actually this will be more clear from official docs
https://arc.net/l/quote/iqulaged
Avatar
      setExercises(prevExercises => {
         const updatedExercises = [...prevExercises];
         updatedExercises[exerciseIndex].sets.push({
            setNumber: updatedExercises[exerciseIndex].sets.length + 1,
            reps: 0,
            weight: 0,
         });
         return updatedExercises;
      });

This is causing the code to runs twce. Dont put side effects inside a setter.