Incrementing value and setting it as var to onClick event

All we need is an easy explanation of the problem, so here it is.

I am facing a really odd problem which causes feel like “whatever you cal”

First of all here is the code:

renderImages = () => {
    let i = 0;
    const arr = [
        "https://truffle-assets.imgix.net/pxqrocxwsjcc_6OcJeUMaWIYOmKgsK6IwWc_galaxy-cupcakes_squareThumbnail_en-US.jpeg",
        "https://sallysbakingaddiction.com/wp-content/uploads/2017/06/moist-chocolate-cupcakes-5.jpg",
        "http://is4.mzstatic.com/image/thumb/Purple127/v4/74/47/31/7447317e-9844-3ee0-d189-661a55c94abe/source/1200x630bb.jpg",
        "https://i.pinimg.com/736x/55/03/94/550394c428e268868aa73e509302b84c--neopolitan-cupcakes-ice-cream-cupcakes.jpg"
    ];

    return arr.map((item) => {
        i++;
        return(
            <div>
                <img src={item} onClick={() => alert(i)} alt={i} />
            </div>
        );
    })
};

Now when i click on an image, it always shows 4. First image shows 4 and last image shows 4. All of them alerts 4. However, their alt tag seems correct. I don’t think i am doing something wrong but i am 🙂

My react version is 16.1.1

So anyone know what am i doing wrong here? I don’t think it is a bind problem but who knows.

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

JavaScript’s scopes are function-level, not block-level, and creating a closure just means that the enclosing scope gets added to the lexical environment of the enclosed function.

After the loop terminates, the function-level variable i has the value 4, and that’s what the inner function ‘sees’.

Now even though let has a block scope the scope of it is limited to the renderImages function and not the map and hence you still have a closure issue.

However map provides you the second argument as an index, which you can use instead of incrementing the variable and using it
like

renderImages = () => {
    const arr = [
        "https://truffle-assets.imgix.net/pxqrocxwsjcc_6OcJeUMaWIYOmKgsK6IwWc_galaxy-cupcakes_squareThumbnail_en-US.jpeg",
        "https://sallysbakingaddiction.com/wp-content/uploads/2017/06/moist-chocolate-cupcakes-5.jpg",
        "http://is4.mzstatic.com/image/thumb/Purple127/v4/74/47/31/7447317e-9844-3ee0-d189-661a55c94abe/source/1200x630bb.jpg",
        "https://i.pinimg.com/736x/55/03/94/550394c428e268868aa73e509302b84c--neopolitan-cupcakes-ice-cream-cupcakes.jpg"
    ];

    return arr.map((item, index) => {
        return(
            <div>
                <img src={item} onClick={() => alert(index)} alt={index} />
            </div>
        );
    })
};

Here is how you can do it for nested arrays

const arr = [[
        "https://truffle-assets.imgix.net/pxqrocxwsjcc_6OcJeUMaWIYOmKgsK6IwWc_galaxy-cupcakes_squareThumbnail_en-US.jpeg",
        "https://sallysbakingaddiction.com/wp-content/uploads/2017/06/moist-chocolate-cupcakes-5.jpg",
        "http://is4.mzstatic.com/image/thumb/Purple127/v4/74/47/31/7447317e-9844-3ee0-d189-661a55c94abe/source/1200x630bb.jpg",
        "https://i.pinimg.com/736x/55/03/94/550394c428e268868aa73e509302b84c--neopolitan-cupcakes-ice-cream-cupcakes.jpg"
    ],[
        "https://truffle-assets.imgix.net/pxqrocxwsjcc_6OcJeUMaWIYOmKgsK6IwWc_galaxy-cupcakes_squareThumbnail_en-US.jpeg",
        "https://sallysbakingaddiction.com/wp-content/uploads/2017/06/moist-chocolate-cupcakes-5.jpg",
        "http://is4.mzstatic.com/image/thumb/Purple127/v4/74/47/31/7447317e-9844-3ee0-d189-661a55c94abe/source/1200x630bb.jpg",
        "https://i.pinimg.com/736x/55/03/94/550394c428e268868aa73e509302b84c--neopolitan-cupcakes-ice-cream-cupcakes.jpg"
    ]];
    
const renderImages = (arr, idx) => {
    return arr.map((item, index) => {

        return(
            <div>
                <img src={item} onClick={() => alert(idx + index)} alt={idx + index} />
            </div>
        );
    })
};
const App = () => {
    let i = 0;
    
    return (
        <div>{arr.map((array, index) => {
            const val = renderImages(array, i);
            i += array.length;
            return <div>{val}</div>
        })}</div>
    )
}



ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"/>

Method 2

Try to modify your code the following way:

return arr.map((item, index) => {

        return(
            <div>
                <img src={item} onClick={() => alert(index)} alt={index} />
            </div>
        );
    })

That way you could remove i completely. Also keep in mind that the index is zero-based so you would maybe want to increment it with 1.

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply