poultrynews
poultrynews

Reputation: 639

How to make the background image change every X seconds in React?

FINAL EDIT:

See working code below:

import React, { Component } from 'react';


var images = [
  "https://www.royalcanin.com/~/media/Royal-Canin/Product-Categories/cat-adult-landing-hero.ashx",
  "https://upload.wikimedia.org/wikipedia/commons/4/4d/Cat_March_2010-1.jpg"
]


class App extends Component {

  constructor(props) {
    super(props);
    this.state = { imgPath: "url(" + images[1] + ")" };
  }

  componentDidMount() {
    this.interval = setInterval(() => {
      this.setState({ imgPath: "url(" + images[0] + ")" })
    }, 1000);
  }

  componentWillUnmount() {
    clearInterval(this.interval);
  }

  render() {
    return (
      <div className="App">
        <div className='dynamicImage' style={{ backgroundImage: this.state.imgPath }} >
          {console.log(this.state.imgPath)}
        </div>
      </div >
    );
  }
}


ORIGINAL THREAD:

I'm trying to use setInterval() to change the image dynamically every X seconds.

I just don't understand where setInterval is supposed to be placed within the code, or what its output is supposed to be.

My current code is:

import React, { Component } from 'react';

// Paths to my images
var images = [
  "https://www.royalcanin.com/~/media/Royal-Canin/Product-Categories/cat-adult-landing-hero.ashx",
  "https://upload.wikimedia.org/wikipedia/commons/4/4d/Cat_March_2010-1.jpg"
]

var imgPath = "url(" + images[1] + ")" // Set original value of path

function f1() {
  imgPath = "url(" + images[0] + ")" // Change path when called ?
}

class App extends Component {
  render() {

    setInterval(f1, 500); // Run f1 every 500ms ?

    return (
      <div className="App">
        <div className='dynamicImage' style={{ backgroundImage: imgPath }} > // Change background image to one specified by imgPath
        </div>
      </div >
    );
  }
}

export default App;

The current code outputs the first imgPath's URL, but fails to update it to the one specified within the function f1. To the best of my knowledge, the function f1 does appear to run, as removing it, or setting an undefined variable does return an error. I just can't get it to change imgPath.

Any ideas on what I'm doing wrong, or how I could improve my code?

Cheers

Edit: Commented code + removed unnecessary lines

Upvotes: 4

Views: 10037

Answers (6)

Nurul Sundarani
Nurul Sundarani

Reputation: 7598

You need to use componentDidMount() React Lifecycle method to register your setInterval function.

Here is a working example

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      images: [
        "https://picsum.photos/200/300/?image=523",
        "https://picsum.photos/200/300/?image=524"
      ],
      selectedImage: "https://picsum.photos/200/300/?image=523"
    };
  }

  componentDidMount() {
    let intervalId = setInterval(() => {
      this.setState(prevState => {
        if (prevState.selectedImage === this.state.images[0]) {
          return {
            selectedImage: this.state.images[1]
          };
        } else {
          return {
            selectedImage: this.state.images[0]
          };
        }
      });
    }, 1000);

    this.setState({
      intervalId
    });
  }

  componentWillUnmount() {
    clearInterval(this.state.intervalId);
  }

  render() {
    return (
      <div className="App">
        <img src={this.state.selectedImage} alt={"images"} />
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);


You can change around the code and can find live demo here: https://codesandbox.io/s/0m12qmvprp

Upvotes: 1

joeyk16
joeyk16

Reputation: 1415

You can create an endless loop similar to this, you might want to use an array of image urls and write some logic for that. But as you can see I have an endless loop created for the function setImage():

  constructor(props) {
    super();

    this.state = {
      image1: props.imageUrls[0],
      image2: props.imageUrls[1],
      changeImage: true
    };

    this.setImage();
  }

  setImage() {
    setTimeout(() => {
      this.setState({ changeImage: !this.state.changeImage }, this.setImage());
    }, 3000);
  }

Upvotes: 1

Jack Templeman
Jack Templeman

Reputation: 358

I would move all your variables into your component and as Akash Salunkhe suggests, use componnentDidMount to setInterval. Don't forget to clear the interval when the component unmounts.

This answer will also work with using any number of images.

class App extends React.Component {
    constructor(props) {
      super(props);

      const images = [
        "https://www.royalcanin.com/~/media/Royal-Canin/Product-Categories/cat-adult-landing-hero.ashx",
        "https://www.petfinder.com/wp-content/uploads/2013/09/cat-black-superstitious-fcs-cat-myths-162286659.jpg",
        "https://upload.wikimedia.org/wikipedia/commons/4/4d/Cat_March_2010-1.jpg"
      ];

      this.state = {
        images,
        currentImg: 0
      }
    }

    componentDidMount() {
      this.interval = setInterval(() => this.changeBackgroundImage(), 1000);
    }

    componentWillUnmount() {
      if (this.interval) {
        clearInterval(this.interval);
      }
    }

    changeBackgroundImage() {
      let newCurrentImg = 0;
      const {images, currentImg} = this.state;
      const noOfImages = images.length;

      if (currentImg !== noOfImages - 1) {
        newCurrentImg = currentImg + 1;
      }

      this.setState({currentImg: newCurrentImg});
    }

    render() {
      const {images, currentImg} = this.state;
      const urlString = `url('${images[currentImg]}')`;

      return (
        <div className="App">
          <div className='dynamicImage' style={{backgroundImage: urlString}} >
          </div>
        </div >
      );
    }
  }

Upvotes: 4

Livinus Umeaduma
Livinus Umeaduma

Reputation: 31

@Anurag is correct. You need to use setInterval in componentDidMount and ensure that you call this.setState if you want the render method to rerender. This of course requires that you store the image path in this.state

Upvotes: 2

Akash Salunkhe
Akash Salunkhe

Reputation: 314

Put image path in state and in componentDidMount, use setInterval and inside it use setState to change image path.

Upvotes: 2

Anurag Srivastava
Anurag Srivastava

Reputation: 14413

You might want to use this.props or this.state to store the imgPath, otherwise React doesn't know you have changed anything.

Upvotes: 2

Related Questions