Hook useState not when event handler is triggered?

80 views Asked by At

The onCardLeftScreen event handler triggers the console log function but doesn't update state using the setState hook? I have tested that this is the issue with an onClick function which runs as intended.

React code:

//sets initial cards within the queue
  const [cardQueue, setCardQueue] = useState([Data[0], Data[1], Data[2]]);

  //sets the index of card that will be pushed into queue
  const [cardQueueLength, setCardQueueLength] = useState(Data.length - 1);

  const autoplayChange = () => {
    setSlideShow(!slideShow);
    console.log("playing!");
  };

  const CardLeftScreen = () => {
    //iterates through cards(sets up loop)
    setCardQueueLength(
      cardQueueLength < Data.length - 1 ? cardQueueLength + 1 : 0
    );

    //removes card from front of queue
    cardQueue.shift();

    //pushes a card to back of queue
    cardQueue.push(Data[cardQueueLength]);

    //sets slideshow to true
    setSlideShow(true);

    //console logs cards in arrays and the index of the card being pushed to back of queue
    console.log(cardQueue);
    console.log(cardQueueLength);
  };

  return (
    <div className="cardStyles">
      {cardQueue.map((Projects, index) => {
        return (
          <TinderCard
            key={Projects.workName}
            preventSwipe={["up", "down"]}
            onCardLeftScreen={CardLeftScreen}
            className="Cards"
          >
            <Carousel
              showThumbs={false}
              infiniteLoop={true}
              swipeable={false}
              emulateTouch={false}
              showStatus={false}
              autoPlay={slideShow}
              dynamicHeight={false}
            >
              <div className="image-iframeContainer">
                {Projects.Images &&
                  Projects.Images.map((Image, index) => {
                    return (
                      <div key={Image} className="image-iframeContainer">
                        <img alt="Images of web apps" src={Image} />
                      </div>
                    );
                  })}
              </div>
            </Carousel>
            <h1>{Projects.workName}</h1>
            {Projects.workTech.map((Tech, index) => {
              return (
                <p key={Tech} className="techList">
                  {Tech}
                </p>
              );
            })}
            <div className="descriptionContainer">
              <p className="description">{Projects.workDescription}</p>
            </div>
          </TinderCard>
        );
      })}
      <button className='cardLeftScreenButton' onClick={CardLeftScreen}>click for onCardLeftScreen desired funtion</button>
    </div>
  );

I have also set up a Sandbox to better display my issue, Hopefully, someone can find a solution my project is so nearly finished. the API used is React-Tinder-Card

4

There are 4 answers

0
lukeet On

The issue was caused due to the fact that the key used was not unique, the onClick function causes react to refresh updating the Dom whereas the onCardLeftScreen function doesn't by default. Setting the key of the Tindercard to key={Projects.workName + Math.random()} fixes the issue as react knows when to refresh. Thanks, everyone for pointing out the state mutation it didn't fix my problem but certainly made my code cleaner

1
SathwikaRao On

whenever you set state in function it doesnt update immediately you can see the change only after event handler comes out of the function.

I think your code is correct and try console log after the function it will show you the intended result and if want to use the updated value use a variable like below

 const CardLeftScreen = () => {
        //iterates through cards(sets up loop)
        let p=
          cardQueueLength < Data.length - 1 ? cardQueueLength + 1 : 0
        

        //removes card from front of queue
        cardQueue.shift();

        //pushes a card to back of queue
        cardQueue.push(Data[p]);

        //sets slideshow to true
        setSlideShow(true);
        
        setCardQueueLength(p);
        //console logs cards in arrays and the index of the card being pushed to back of queue
        console.log(cardQueue);
        console.log(cardQueueLength);
      };
      
          console.log("you can see the change here",cardQueueLength);//you can see the change here
1
zero298 On

You are mutating your state array directly instead of duplicating it and affecting that array. This means that React doesn't think your array has actually changed. Consider the change below:

const {useState} = React;

const mockData = [
    {title: "Foo", description: "Hello World!"},
    {title: "Bar", description: "Fizz Buzz!"},
    {title: "Cool", description: "More Stuff!"}
];

const MyComponent = () => {
    const [cards, setCards] = useState([mockData[0], mockData[1], mockData[2]]);
    const [nextDataIndex, setNextDataIndex] = useState(mockData.length - 1);

    const onCardLeftScreen = () => {
        const newNextDataIndex = nextDataIndex < mockData.length - 1 ? nextDataIndex + 1 : 0;

        const newCards = [...cards];
        newCards.shift();
        newCards.push(mockData[newNextDataIndex]);

        setNextDataIndex(newNextDataIndex);
        setCards(newCards);
    };

    return (
        <div>
            <div>Num cards: {cards.length}</div>
            <div className="card-container">
                {cards.map(card => (
                    <div key={card.title} className="card">
                        <div>{card.title}</div>
                        <div>{card.description}</div>
                    </div>
                ))}
            </div>
            <button onClick={onCardLeftScreen}>Swipe Left</button>
        </div>
    );
};

const App = () => (
    <MyComponent />
);

ReactDOM.render(
    <App/>,
    document.getElementById("app")
);
.card-container {
  width: 100px;
  height: 100px;
  position: relative;
}
.card {
  background-color: #0000AA;
  color: #ffffff;
  padding: 0.2em;
  width: 50px;
  height: 50px;
  box-shadow: 2px 2px black;
  position: absolute;
  top: 0;
  left: 0;
}
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="app"></div>

2
Ross Sheppard On

Try something like this. You are mutating state directly which is not good and can lead to all sorts of side effects and mishaps, and maybe the reason you might see the same cards twice in a row, lag, etc.

  // sets initial cards within the queue
  const cards = [ Data[0], Data[1], Data[2] ];
  const [cardQueue, setCardQueue] = useState(cards);
  const [cardQueueLength, setCardQueueLength] = useState(Data.length - 1); //sets the index of card that will be pushed into queue

  const autoplayChange = () => {
    setSlideShow(!slideShow);
    console.log("playing!");
  };

  //iterates through cards(sets up loop)
  const handleCardLeave = () => {
    const newCardQueueLength = cardQueueLength < Data.length - 1 ? cardQueueLength + 1 : 0;
    setCardQueueLength(newCardQueueLength);

    const updatedCardQueue = [...cardQueue]; // don't mutate state directly
    updatedCardQueue.shift(); // removes card from front of queue
    updatedCardQueue.push(Data[cardQueueLength]); // pushes a card to back of queue
    setCardQueue(updatedCardQueue);

    //sets slideshow to true
    setSlideShow(true);

    //console logs cards in arrays and the index of the card being pushed to back of queue
    console.log(cardQueue);
    console.log(cardQueueLength);
  };