0

In react/nextjs I have click Event for a button I'm trying to use setState and change an array and it fires up after two clicks Here is the state array I have

const [arrayClicked, setArrayClicked] = useState(["image-id-0", "image-id-1", "image-id-2", "image-id-3"]);

["image-id-0", "image-id-1", "image-id-2", "image-id-3"] so the plan is to remove the first element and push in a new element from the back so it would be ["image-id-1", "image-id-2", "image-id-3", "image-id-4"] then ["image-id-2", "image-id-3", "image-id-4", "image-id-5"] etc

So if I use the JS mutation of the array and not the setState as:

arrayClicked.shift();
arrayClicked.push("image-id-4");
// I know that this is not the correct way of working with React but it works from the first click

this works directly from the first click of the button. But when I try to use setState I have to press twice for it fire up

this is my setState code:

const newIDImg = "image-id-4"; // example normally they are actual ID values for images
setArrayClicked(prev => {
  const prevArray = prev.filter(val => {
    return val !== prev[0];
  });
  return [...prevArray, newIDImg];
});

// even if I try slice same problem it fires up after second click
setArrayClicked(prev => [
  ...prev.slice(1, prev.length),
  newIDImg
]);

Here is the full set of code:


import { useState } from "react";

// props.product.images is 13 in length so index from 1 to 13 so keeping it as read only

const [displaySmallerImages, setDisplaySmallerImages] = useState(props.product.images.slice(0, 4));
const [arrayClicked, setArrayClicked] = useState(['image-id-0', 'image-id-1', 'image-id-2', 'image-id-3']);

const handleRightClick = () => {
    // example props.product.images is 13 in length
    const exampleFakeIndex = 3;
    let newIDImg, tempArray;

    if (props.product.images[exampleFakeIndex + 1] !== undefined) {
        newIDImg = props.product.images[exampleFakeIndex + 1].id;
        // arrayClicked.shift(); // TODO FIX THIS TO USE setArrayClicked INSTEAD!!
        // arrayClicked.push(newIDImg); // TODO FIX THIS TO USE setArrayClicked INSTEAD!!
        setArrayClicked(prev => {
            const prevArray = prev.filter(val => {
                return val !== prev[0];
            });
            return [...prevArray, newIDImg];
        });
        // AS displaySmallerImages IS ARRAY OF OBJECT THIS IS A HELPER FUNCTION WHICH DISPLAYS THEM AS
        tempArray = helperArrayNewSetOfFours(arrayClicked, props.product.images);
        // helperArrayNewSetOfFours just returns an array of object of {id, imgName, imgData}
        setDisplaySmallerImages(tempArray);
    } else {
        newIDImg = props.product.images[0].id;
        // arrayClicked.shift(); // TODO FIX THIS TO USE setArrayClicked INSTEAD!!
        // arrayClicked.push(newIDImg); // TODO FIX THIS TO USE setArrayClicked INSTEAD!!

        setArrayClicked(prev => [
            ...prev.slice(1, prev.length),
            newIDImg
        ]);

        tempArray = helperArrayNewSetOfFours(arrayClicked, props.product.images);
        setDisplaySmallerImages(tempArray);
    }
}

The helperFunction helperArrayNewSetOfFours is basically a function with returns an array of object for the new array of objects to be displayed and then passed to the displaySmallerImages array through the setDisplaySmallerImages incase you want to see what the helperArrayNewSetOfFours is:


    const helperArrayNewSetOfFours = (oldFour, fullArraySize) => {
        const arrayTemp = [];
        oldFour.forEach((val, ind) => {
            fullArraySize.forEach(img => {
                if (val === img.idImage) {
                    arrayTemp.push({
                        id: img.id,
                        imgName: img.imgName,
                        imgData: img.imgData
                    });
                }
            });
        });
        return arrayTemp;
    }

returns:

//THE OLD
array = [ 
 { id: "image-id-1", imgName: "name1", imgData: ARRAY BUFFER },
 { id: "image-id-2", imgName: "name2", imgData: ARRAY BUFFER },
 { id: "image-id-3", imgName: "name3", imgData: ARRAY BUFFER },
 { id: "image-id-4", imgName: "name4", imgData: ARRAY BUFFER }
];

// The new array because of the helper function returns
//THE NEW IT RETURNS!!
array = [ 
 { id: "image-id-2", imgName: "name2", imgData: ARRAY BUFFER },
 { id: "image-id-3", imgName: "name3", imgData: ARRAY BUFFER },
 { id: "image-id-4", imgName: "name4", imgData: ARRAY BUFFER },
 { id: "image-id-5", imgName: "name5", imgData: ARRAY BUFFER }
];

So why does setState force two click before the values registers and it fires up? While array mutation JS the .shift() and .push() fires up directly from the first click? How can I make the setState to work directly from the first click?

juliomalves
  • 42,130
  • 20
  • 150
  • 146
Mohamed
  • 425
  • 4
  • 14
  • 1
    Ranting about closure in your question will probably not get the best response from the community. I just took a look at the suggested duplicate and the problem and solution appear to be the same as yours. – Jason Goemaat Jan 04 '23 at 03:43

1 Answers1

1

SOLVED

For anyone who wants to know I learned from this that you cannot use setState from the result of a state you just setted.

For example I have setArrayClicked which I am setting it's state with new values then I wanted to place arrayClicked into setDisplaySmallerImg which caused the event to fire after a second click. Because arrayClicked is the yet the old values and setState is asynchronous so the new values haven't been stored yet.

So instead of this:

        setArrayClicked(prev => [
            ...prev.slice(1, prev.length),
            newIDImg
        ]);

        tempArray = helperArrayNewSetOfFours(arrayClicked, props.product.images);
        setDisplaySmallerImages(tempArray);

I done:

// made a temp tempFour array
let tempFour = [];

            tempFour = [...arrayClicked.slice(1, forClick.length), newIDImg];
// so here I rather using the tempFour array and putting that value into the setDisplaySmallerImages
// then as well setting the setArrayClicked with the tempFour array so for the next click I have new values in the arrayClicked so everything is going smoothly from the first click now!
            setArrayClicked(tempFour);

            tempArray = helperArrayNewSetOfFours(tempFour, props.product.images);
            setDisplaySmallerImages(tempArray);
Mohamed
  • 425
  • 4
  • 14