frootloops
frootloops

Reputation: 291

How do you shuffle a table of images every second on the click of a button?

I have a bootstrap table with images and names. My data is an array of objects containing image URLs and names. When I click the button I want the image objects to shuffle every 1 second. I am using the Fisher-Yates algorithm to shuffle inside of a setInterval() function.

I would also like to know how to create a stop button.

mock-data:

export const data = [{
    url: 'https://via.placeholder.com/80/FF0000',
    name: 'ben'
  },
  {
    url: 'https://via.placeholder.com/80/000000',
    name: 'jon'
  },
  {
    url: 'https://via.placeholder.com/80/FFFFFF',
    name: 'sam'
  },
  {
    url: 'https://via.placeholder.com/80/0000FF',
    name: 'bill'
  },
  {
    url: 'https://via.placeholder.com/80/008000',
    name: 'tom'
  }
];

Here is my Component:

import React, { Component } from 'react';
import { Table, Button } from 'reactstrap';
import './App.css';
import { data } from './mock-data';
import { shuffle } from './helpers/shuffle';

class App extends Component {
  constructor(props){
    super(props)
     this.state = {
      images: data
     }
   }
   handleStartShuffle = () => {
     this.setState({images: setInterval(shuffle(this.state.images), 1000)});
   }

   render () {
     const imageTable = this.state.images.map((item, index) => {
       console.log('item.url', item.url)
       return (
       <tr key={index}>
         <td>
            <img src={item.url} alt='random' />
         </td>
         <td>{item.name}</td>
       </tr>
       )
     })
     return (
       <div>
         <Table>
           <thead>
              <tr>
                <th>image</th>
                <th>name</th>
              </tr>
           </thead>
           <tbody>
              {imageTable}
           </tbody>
         </Table>
         <Button onClick={this.handleStartShuffle}>Start</Button>
       </div>
     );
   }
 }
export default App;

Here is my helper function for shuffling:

export function shuffle(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * i);
    const temp = array[i];
    array[i] = array[j];
    array[j] = temp;
  }
  return array;
}

Upvotes: 2

Views: 771

Answers (3)

rickdenhaan
rickdenhaan

Reputation: 11328

@TShort already showed you exactly how to keep track of the interval and stop it properly, so I'm not going to copy/paste that here.

But, your shuffle function is currently modifying the array in-place. Since the array is in your component's state, this can lead to unpredictable results. You'll want to change your function to return a new array.

The simplest solution is to make a copy at the start of the function:

export function shuffle(oldArray) {
  const array = [...oldArray];

Upvotes: 1

user1039663
user1039663

Reputation: 1343

setInterval returns a timer id, then you can call to clearInterval with that id. You example is a little blurry, specially when you store the interval id in state.images. See Using setInterval in React Component

Upvotes: 0

T. Short
T. Short

Reputation: 3614

I would do it like this:

let interval;
handleStartShuffle = () => {
   interval = setInterval(() => {
        this.setState({images: shuffle(this.state.images)});
   }, 1000);
}
stopShuffle = () => {
   if(interval) {
       clearInterval(interval);
   }
}

Upvotes: 2

Related Questions