Is there a way to avoid using forceupdate in my code?

Viewed 52

I have a block of code like this:

    randomArray(arrayOfImage){
        //algorithm goes here
        return shuffledArray;
}

    shuffle(){       
        this.randomArray(images);
        this.forceUpdate();
} 

    render(){
        let array = this.randomArray(images);       //images is a declared array
        let squares = array.map((item,index) => <Squares/> )

        return(
            <div>
                <div id='square-container'>
                    {squares}
                </div>
                <button className='btn' onClick = {this.shuffle.bind(this)}>Shuffle</button>
            </div>
        )
    }

Basically, I have an array images declared. The function randomArray() return a shuffled version of images. And then, for each of the item inside the shuffled array array the browser will render a div with the given props.

Now I have a button so that the user can shuffle the array themselves. Here, I use forceupdate() because even though the array is shuffled when the button is clicked, the DOM won't update because there is no changes in the state.

It works! But since using forceupdate() is not encouraged, how should I make this less... say, amateur?

1 Answers

Sure, React will render an element again as soon as it's state gets changed, or it receives new props from it's parent component (or a state library).

You can read more about how react handles the rendering in their documentation

So, to handle this, just create a new array based on your original one, and then set it to the state again.

In the olden days, you would require a class for setting the state on component level

const target = document.getElementById('container');

class ArrayOfImages extends React.Component {
  constructor() {
    // don't forget to call super
    super();
    // set the initial state
    this.state = {
      images: [
      'https://c402277.ssl.cf1.rackcdn.com/photos/18357/images/featured_story/Medium_WW252652.jpg?1576698319',
      'https://c402277.ssl.cf1.rackcdn.com/photos/882/images/circle/African_Elephant_7.27.2012_hero_and_circle_HI_53941.jpg?1345532748',
      'https://c402277.ssl.cf1.rackcdn.com/photos/1732/images/circle/Asian_Elephant_8.13.2012_Hero_And_Circle_HI_247511.jpg?1345551842'
      ]
    };
    // bind the function we are going to call from the render function
    this.shuffle = this.shuffle.bind( this );
  }
  shuffle() {
    // create a shallow copy of the state object
    const copyOfState = this.state.images.slice();
    // shuffle it a bit (Durstenfeld shuffle: http://en.wikipedia.org/wiki/Fisher-Yates_shuffle#The_modern_algorithm)
    for (let i = copyOfState.length; i--;) {
      let target = Math.floor( Math.random() * (i+1) );
      [copyOfState[i], copyOfState[target]] = [copyOfState[target], copyOfState[i]];
    }
    // update the existing state with the new array
    this.setState({ images: copyOfState });
  }
  render() {
    const { images } = this.state;
    return (
      <div>
        <h1>Some elephants</h1>
        { images.map( img => <img key={ img } alt={ img } src={ img } /> ) }
        <div><button type="button" onClick={ this.shuffle }>Shuffle images</button></div>
      </div>
    );
  }
}

ReactDOM.render( <ArrayOfImages />, target );
<script id="react" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.2/react.js"></script>
<script id="react-dom" src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.6.2/react-dom.js"></script>
<div id="container"></div>

But now, you could do it by using the useState helper for that

// this would be an import in normal code
const { useState } = React;

const target = document.getElementById('container');

const shuffle = arr => {
  // create a shallow copy of the array
  const copy = arr.slice();
  // shuffle it a bit (Durstenfeld shuffle: http://en.wikipedia.org/wiki/Fisher-Yates_shuffle#The_modern_algorithm)
  for (let i = copy.length; i--;) {
    let target = Math.floor( Math.random() * (i+1) );
    [copy[i], copy[target]] = [copy[target], copy[i]];
  }
  return copy;
}

const ArrayOfImages = () => {
  const [images, updateImages] = useState([
    'https://c402277.ssl.cf1.rackcdn.com/photos/18357/images/featured_story/Medium_WW252652.jpg?1576698319',
    'https://c402277.ssl.cf1.rackcdn.com/photos/882/images/circle/African_Elephant_7.27.2012_hero_and_circle_HI_53941.jpg?1345532748',
    'https://c402277.ssl.cf1.rackcdn.com/photos/1732/images/circle/Asian_Elephant_8.13.2012_Hero_And_Circle_HI_247511.jpg?1345551842'
  ]);
  return (
    <div>
      <h1>Some elephants</h1>
      { images.map( img => <img key={ img } alt={ img } src={ img } /> ) }
      <div><button type="button" onClick={ () => updateImages( shuffle( images ) ) }>Shuffle images</button></div>
    </div>
  );
}

ReactDOM.render( <ArrayOfImages />, target );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="container"></div>

The shuffle algorithm comes from this answer

Related