Reactjs slider causes unexpected useState behavior

I created an autoplay Slider with three cards using the useEffect hook. However, the manual "previous" and "forward" buttons are not functioning correctly. The useState function is not updating values as expected, leading to unexpected changes in state. When I manually swipe the slides by clicking on the previous or forward arrow, the slides start changing rapidly. The auto slider works perfectly fine, but when I manually swipe, the state changes unexpectedly. How can I implement both auto-play and manual control (including forward and backward arrows) so that the state transitions smoothly?

import React, { useState, useRef, useEffect } from "react";
import { Card, Grid } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";
import { SlideData, SliderImages } from "./SlideData";
import left from "../../../styles/img/homepg/AdmSlider/left.png";
import right from "../../../styles/img/homepg/AdmSlider/right.png";


function NewsSlider() {
  const classes = useStyles();

  const firstRef = useRef(null);
  const secondRef = useRef(null);
  const thirdRef = useRef(null);
  const currentRef = useRef(null);


  const Left = () => {
    setFirst(first <= 0 ? len : first - 1);
    setSecond(second <= 0 ? len : second - 1);
    setThird(third <= 0 ? len : third - 1);
  };

  const Right = () => {
    setFirst(first >= len ? 0 : first + 1);
    setSecond(second >= len ? 0 : second + 1);
    setThird(third >= len ? 0 : third + 1);
  };

  const [first, setFirst] = useState(0);
  const [second, setSecond] = useState(1);
  const [third, setThird] = useState(2);

  const length = SliderImages.length;
  const len = length - 1;
  let timeout;
  
  useEffect(() => {
    setTimeout(() => {
      setFirst(first >= len ? 0 : first + 1);
      setSecond(second >= len ? 0 : second + 1);
      setThird(third >= len ? 0 : third + 1);
       return () => clearTimeout(timeout);
    }, 3000);
  }, [first, second, third]);

  return (
    <>
      <div>
        <Grid container xs={12} className={classes.grid}>
          {" "}
          <div>
            <img
              src={left}
              alt="leftarrow"
              className={classes.left}
              onClick={Left}
            />
          </div>
          <Grid item xs={4} className={classes.card}>
            {SliderImages.map((val, index) => {
              return (
                <div>
                  {index === first && (
                    <Card className={classes.card1}>
                      <img
                        src={val.imgsrc}
                        alt={val.title}
                        className={classes.image}
                        ref={firstRef}
                      />
                    </Card>
                  )}
                  {index === second && (
                    <Card className={classes.card2}>
                      <img
                        src={val.imgsrc}
                        alt={val.title}
                        className={classes.image}
                        ref={secondRef}
                      />
                    </Card>
                  )}
                  {index === third && (
                    <Card className={classes.card3}>
                      <img
                        src={val.imgsrc}
                        alt={val.title}
                        className={classes.image}
                        ref={thirdRef}
                      />
                    </Card>
                  )}
                  <div>
                    <img
                      src={right}
                      alt="rightarrow"
                      className={classes.right}
                      onClick={Right}
                    />
                  </div>
                </div>
              );
            })}
          </Grid>
        </Grid>
      </div>
    </>
  );
}

export default NewsSlider;

Answer №1

Problem

The issue lies in initializing a new timeout without clearing the previous one when manually updating first, second, or third. Additionally, redeclaring timeout within the function body leads to its constant renewal during each render cycle.

Solution

To mitigate this, store the timeout reference and return the clear function from the useEffect callback instead of within the timeout's callback.

useEffect(() => {
  const timeout = setTimeout(() => {
    setFirst(first >= len ? 0 : first + 1);
    setSecond(second >= len ? 0 : second + 1);
    setThird(third >= len ? 0 : third + 1);
  }, 3000);

  return () => clearTimeout(timeout);
}, [first, second, third]);

Alternative Approach

An alternative method involves constructing a slider animation on an interval with functional state updates to manage previous or next slides dynamically. This enables the use of an interval timer alongside manual sliding in any direction while maintaining consistent slide transitions.

const MoveLeft = () => {
  setFirst(first => first <= 0 ? len : first - 1);
  setSecond(second => second <= 0 ? len : second - 1);
  setThird(third => third <= 0 ? len : third - 1);
};

const MoveRight = () => {
  setFirst(first => first >= len ? 0 : first + 1);
  setSecond(second => second >= len ? 0 : second + 1);
  setThird(third >= len ? 0 : third + 1);
};

const [first, setFirst] = useState(0);
const [second, setSecond] = useState(1);
const [third, setThird] = useState(2);

const length = SliderImages.length || 0;
const len = length - 1;

useEffect(() => {
  const interval = setInterval(() => {
    MoveRight();
  }, 3000);

  return () => clearInterval(interval);
}, []);

Answer №2

Consider integrating arrow functions into your useState hook for better functionality

  const MoveLeft = () => {
    setFirst(first => first <= 0 ? len : first - 1);
    setSecond(second => second <= 0 ? len : second - 1);
    setThird(third => third <= 0 ? len : third - 1);
  };

  const MoveRight = () => {
    setFirst(first => first >= len ? 0 : first + 1);
    setSecond(second => second >= len ? 0 : second + 1);
    setThird(third => third >= len ? 0 : third + 1);
  };

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

how to customize nested styles in material-ui

I am facing an issue with a basic Material-UI component <Chip avatar={ <Avatar> <TruckIcon color='primary' /> </Avatar> } label={name} color='primary' /> ...

What is the best way to embed two controllers within an AngularJS webpage?

Currently, I have a Web Forms ASP.NET website that I am trying to enhance by adding an AngularJS page. This page is meant to interact with my RESTful Web API to display quotes for selected securities upon button click. While the Web API calls work when dir ...

waiting to display information until it is necessary

I am currently working on optimizing my website for improved loading speed and responsiveness. Users can scroll through up to 4k images, apply filters, and sort them based on their preferences. Below is the code snippet for my filtering function: function ...

What is the process for converting a UTC datetime string into the local timezone of users?

I am utilizing Laravel 5.7 for the API and Vue for the frontend development. The API response includes a user's last seen timestamp in UTC format by default, like this: last_seen: "2019-04-17 05:20:37". Laravel API code: $user_details = array( ...

Navigating through tabs in a Meteor application: How to maintain the active tab when using the back button

I am working on a multi-page meteor application where each page includes a navigation template. To switch between pages, I am using iron-router. The user's current page is indicated by setting the appropriate navigation link's class to 'ac ...

What is the method or variable called "afterShow" used for in FancyBox V4 and how does it differ from its counterpart in JQuery-FancyBox V3?

We previously utilized the V3 edition of Fancybox, incorporating our increaseImageClicks and increaseVideoClicks functions within its afterShow function: /* FANCYBOX OLD (https://web.archive.org/web/20210325170940/https://fancyapps.com/fancybox/3/docs/): * ...

Encountered an error while configuring process.env.secrets when deploying Next.js with AWS Amplify

Hello, I am facing an issue while trying to host a web/app with AWS Amplify. Despite my efforts, it doesn't seem to work. Here is the error I'm encountering after running the code with yarn and removing package.lock.json: The error log includes ...

What is the best way to locate the position of a different element within ReactJS?

Within my parent element, I have two child elements. The second child has the capability to be dragged into the first child. Upon successful drag and drop into the first child, a callback function will be triggered. What is the best way for me to determi ...

Creating a worldwide entity through the use of an Immediately Invoked Function Expression

There are 2 methods I discovered for defining a global object using IIFE: (function () { var func = function () { }; window.func = func; }()); compared to: (function (myFunc) { window.func = myFunc(); }(function () { var func = functi ...

retrieve the current image source URL using JavaScript

In the template below, I am looking to extract the current img src URL and utilize it in a fancybox button. For example, in the template provided, there are 3 images from https://farm6.staticflickr.com. When clicking on these images, the fancybox will ope ...

Utilizing Angular 1.4.8 and lodash to effectively parse an array of objects with conditional parameters

DEVELOPER TOOLS Angular version 1.4.8, lodash version 4.0 SOLUTION IMPLEMENTATION After building on Derek's code contribution below, I have arrived at the following solution. It was necessary to make adjustments as using _.property() or _.map() ch ...

Exploring the directories: bundles, lib, lib-esm, and iife

As some libraries/frameworks prepare the application for publishing, they create a specific folder structure within the 'dist' directory including folders such as 'bundles', 'lib', 'lib-esm', and 'iife'. T ...

Turning a regular folder into a ReactJS application: A beginner's guide

I'm in the process of developing a react js app, so I decided to set up node js on my Windows machine and ran the command: npm install -g create-react-app in the directory 'C:\Users\Desktop\ReactDemo'. However, the result plac ...

Is there a hashing algorithm that produces identical results in both Dart and TypeScript?

I am looking to create a unique identifier for my chat application. (Chat between my Flutter app and Angular web) Below is the code snippet written in Dart... String peerId = widget.peerid; //string ID value String currentUserId = widget.currentId ...

An unforeseen vow in lieu of an assortment

I'm currently working on a project involving node and mongo. To achieve parallel requests, I am utilizing a netlify serverless function that will be built using data from mongo records. Here's what I have done so far: paralellNum = 2; const filt ...

Switch out the checkboxes in Material-Table for radio buttons instead

Currently utilizing the material-table to display data. My goal is to select one row at a time and pass that row's data to another component. Multi-row selection is not desired, so using radio buttons instead of checkboxes is the suggested solution. I ...

DataTables.js, the powerful JavaScript library for creating interactive tables, and its compatible counterpart

I'm currently making some changes to a dynamic table that require enabling a vertical scroll bar. As a result, I need to implement this vertical scroll bar mechanism on an existing table. The code in our project utilizes dataTables.js version 1.5.2 ...

The Node is unable to initiate the react index.js file, resulting in an exit with code

Today, I encountered an issue while trying to start my React project. It seems like npm is unable to locate my index.js file, even though it was working perfectly fine yesterday. I have already cleared the npm cache and reinstalled the node modules. The co ...

How to align content within a FormControlLabel using Material UI

My goal is to line up the label and radio button inside a FormControlLabel component so that the label occupies the same width regardless of its content. The current appearance can be seen below: https://i.stack.imgur.com/GhXed.png Below is the code I am ...

Learning how to handle URLEncoded format in Vue JS

Seeking guidance on how to handle URL Encoded format in postman to send data to my Vue JS app. Using the encoded format shown below, what npm package should I utilize for this task? https://i.stack.imgur.com/sBkXi.png ...