How to use the key attribute do in an img tag?

2.6k Views Asked by At

I have this code to make an array of images to display on the screen. However, I don't know what the key attribute in the images array means. Ideally I want to change the src of the image whenever one of the images is clicked. If I add in an id or className property to the image, and look it up using document.getElementById i get this warning: Warning: Prop `%s` did not match. Server: %s Client: %s%s when I rendered the page. I am using react and razzle for this project. Can someone tell me how to accomplish this?

var shuffle = require("shuffle-array"),
    animals = [
      "frog0",
      "frog1",
      // ...
      "seal1",
      "armadillo0",
      "armadillo1"
    ];

function whatami(img) {
  alert(img);
}
var images = shuffle(animals).map(image => {
  return (
    <img
      onClick={() => {
        whatami(image);
      }}
      key={image}
      src={"/animalgameback.jpg"}
    />
  );
});
const App = () => (
  <div>
    <h3>My razzle app</h3>
    <div>{images}</div>
  </div>
);

export default App;
2

There are 2 best solutions below

0
On

The key attribute serves as an identity declaration. It helps rendering engine to decide which elements should be re-rendered.

It's well explained in the documentation.

0
On

There quite a few things wrong with your approaches here. I would highly suggest reading through the official React documentation on how to write Javascript in ReactJS.

Let's cover some basics. Firstly, you really should not ever be using document.getElementById in React (unless the situation is dire and you are trying to hack apart a third party library). For the most part, you use the prop ref to reference React Nodes that are mounted in the DOM. But, just some advice for those learning, have your fun with using references so that you know how to use them and what they do. But. I would suggest that if you "need" a reference or "need" to directly talk to a React component at run-time, you might be doing something wrong.

Now, since you are attempting to "change" something based on user events or interactions, this is a perfect use-case for state management. React comes with the ability for each component to self-encapsulate a stateful object and use these states to "trigger" or re-render things in components, due to this update or change.

What is a key? It is a unique identifier that you can use on each rendered JSX component that shows the virtual DOM and the real DOM, that this component is intended to be re-rendered as is rather than unmounted, changed and re-mounted. A key allows React to keep track of which components were intended versus just respawned or mounted. You always write a key of a JSX element as a unique id. If you made 2 id's the same (try it out and see :) ) you would notice that they render as 1 on the screen and one replaces the other.

Heres how I would write this: I have made a single image as a "viewer" to show which image was clicked, along with a click event handler attached to the image to change the state. The render function detects the image source change and re-renders the component. Therefore, the new source is received and used and rendered.

The Code

import React, { Component } from 'react';

const ANIMAL_DATA = [
    'frog0','frog1','sheep0','sheep1','snail0','snail1','mouse0','mouse1','bat0','bat1','walrus0',
    'walrus1','giraffe0','giraffe1','zebra0','zebra1','dog0','dog1','octopus0','octopus1','hippo0',
    'hippo1','camel0','camel1','pig0','pig1','rhino0','rhino1','rooster0','rooster1','panda0','panda1',
    'turtle0','turtle1','raccoon0','raccoon1','polarbear0','polarbear1','lion0','lion1','bison0',
    'bison1','orca0','orca1','snake0','snake1','shark0','shark1','toucan0','toucan1','butterfly0',
    'butterfly1','anteater0','anteater1','seal0','seal1','armadillo0','armadillo1'
]

class App extends Component {

    constructor(props) {
        super(props);

        this.state = {
            imageSource: 'animalgameback',
        };
    }
    
    render() {
        const { imageSource } = this.state;

        return (
            <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}>
                <img style={{ width: 143, height: 'auto' }} source={require(`/${imageSource}.jpg`)} />

                { this.renderAnimalImages() }
            </div>
        );
    }

    renderAnimalImages() {
        let images = [];

        ANIMAL_DATA.forEach((animal, animalIndex) => {
            // Be careful when assigning "onclick" methods to images,
            // you are better off sticking with W3C rules on this. Use elements
            // meant for "clicking" or "being clicked", i.e. <a>, <button>, etc
            images.push(
                <img
                    src={`/${animal}.jpg`}
                    key={`anima_image_${animalIndex}`}
                    onClick={this.__handleImageClick(animal)} />
            );
        });

        return images;
    }


    __handleImageClick = animal => event => {
        this.setState({ imageSource: animal });
    };
}


export default App;