0

I have the following code (obviously this is just a test case for the question, life is more complicated...). This code works, but it gives the Warning:

Each child in an array or iterator should have a unique "key" prop.

I'm quite new to React and javascript, but I do understand the error, and I do understand why unique keys are needed. As you can see I am indeed providing keys and they are indeed unique

var possible_variants = ["A", "B", "C"];
var variant = "";

class App extends React.Component {
  render() {
    return React.createElement(
      "div",
      null,
      React.createElement(RadioButton, null)
    );
  }
}

class RadioButton extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    variant = event.target.value;
  }

  render() {
    return React.createElement(
      "div",
      {
        onChange: this.handleChange
      },

      possible_variants.map(function(my_variant, index) {
        return React.createElement(
          "label",
          null,
          React.createElement("input", {
            type: "radio",
            value: my_variant,
            key: my_variant,
            name: "variant"
          }),
          my_variant
        );
      })
    );
  }
}

ReactDOM.render(
  React.createElement(App, null),
  document.getElementById("container")
);

The problem seem to be that React "wants" the key be in the wrapping label instead, i.e. if I change the mapping to

possible_variants.map(function(my_variant, index) {
  return React.createElement(
    "label",
    {
      key: my_variant // <= here comes the difference
    },
    React.createElement("input", {
      type: "radio",
      value: my_variant,
      name: "variant"
    }),
    my_variant
  );
});

the warning goes away.

So my question is why it is so? It was my understanding from the official documentation and from various tutorials around the net that for best performance one wants the key being into the most specific thing which may change for re-rendering, and that is the input here, not the label which is fixed once and for all.

Am I missing something?

PS: To me, this feels similar to React unique key when mapping warning

keikai
  • 14,085
  • 9
  • 49
  • 68
Davide
  • 17,098
  • 11
  • 52
  • 68
  • You need to give a key attribute to the root element returned from `map`, with a unique value for each – Yosef Tukachinsky Mar 03 '20 at 13:15
  • read about reconciliation in React, also when React checks what has changed he must check the root elements of the returned array by map function. Also why don't you use JSX it's much more convenient – Alopwer Mar 03 '20 at 13:18
  • @YosefTukachinsky because I using this from Transscrypt and the setup I currently have does not work, see https://github.com/QQuick/Transcrypt/issues/709 – Davide Mar 04 '20 at 14:06

1 Answers1

1

First, a unique key is needed, as we both know.

As for the code, the first one actually didn't make the key binding to map items, as well as the second one, it did.

Refer to React.createElement document, each creates one level of DOM element.

In your first code, it actually created twice, which means your key only been bound inside of the map items, the map items right under list.map haven't got any keys.

<label>
  <input key={...}/>
</label>

The second one made it as normal, so it comes to the difference.

<label key={...}>
  <input />
</label>

By the way, as the comment said, you rarely have the chance to use those react top-level API, JSX/TSX just makes it simpler.

Related QA in specific.

keikai
  • 14,085
  • 9
  • 49
  • 68