3

I have noticed that I can pass to React a set of nested arrays and it will render the items properly, but when I go this way, it will not complain of missing keys on my elements.

const stuff = 'a,b,c';

// Nested Array
// React is fine with it and automatically assigns keys
// Sample data: [[a, <br />], [b, <br />], [c, <br />]]
const Foo = () => <div>{stuff.split(',').map(itm => [itm, <br />])}</div>;

// Flat Array
// React warns me that I should assign a key to each element in array
// Sample data: [a, <br />, b, <br />, c, <br />]
const Bar = () => <div>{stuff.split(',').map(itm => [itm, <br />]).reduce((a, b) => a.concat(b), [])}</div>;

Sample pen:

https://codepen.io/FezVrasta/pen/NppLPR

Why does it happens? I can't find any reference to "nested arrays" support in React, neither on "auto assigned keys".

1
  • For Arrays of children, use keyed fragments. The docs acknowledge it's not possible to add a key attribute to a native Array and offers the 'react-addons-create-fragment' package in order to handle them. Commented Mar 14, 2017 at 17:18

2 Answers 2

4
+50

You'll notice that even though a warning is printed to the console, React still shows both Foo and Bar in your HTML being generated. React uses unique keys for reconciliations whilst trying to boost rendering performance. You can read more about this on the React reconciliation recursing on children page. Not providing keys means React cannot be as performant as it has been designed to be.

With regards to your question as to why a warning is not output to the console for nested arrays, we have to dive into the source code:

The function which generates the warning is called validateExplicitKey, and lives in the ReactElementValidator.js module.

This function is used in the validateChildKeys in the same module - looking into the source code gives the following, as of React 15.4.2:

function validateChildKeys(node, parentType) {
  if (typeof node !== 'object') {
    return;
  }
  if (Array.isArray(node)) {                     // 1.
    for (var i = 0; i < node.length; i++) {
      var child = node[i];                       // 2.
      if (ReactElement.isValidElement(child)) {  // 3.
        validateExplicitKey(child, parentType);
      }
    }
  } else if (ReactElement.isValidElement(node)) {
    // This element was passed in a valid location.
    if (node._store) {
      node._store.validated = true;
    }
  } else if (node) {
    var iteratorFn = getIteratorFn(node);
    // Entry iterators provide implicit keys.
    if (iteratorFn) {
      if (iteratorFn !== node.entries) {
        var iterator = iteratorFn.call(node);
        var step;
        while (!(step = iterator.next()).done) {
          if (ReactElement.isValidElement(step.value)) {
            validateExplicitKey(step.value, parentType);
          }
        }
      }
    }
  }
}
  1. An array of arrays will enter the first code block
  2. the child will be set to child = ["b", Object] (where 'Object' is react's virtual dom representation for the br node we created via JSX)
  3. the array will be run through the function ReactElement.isValidElement:

    ReactElement.isValidElement = function (object) {
      return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
    };
    

with REACT_ELEMENT_TYPE being set as:

var REACT_ELEMENT_TYPE = typeof Symbol === 'function' && Symbol['for'] && Symbol['for']('react.element') || 0xeac7;

The array is an object, and is not null, but it's $$typeof property hasn't been set here, so the check fails.

$$typeof hasn't been set because React only adds this property to elements it creates to identify whether something is a React Element or not. This includes native HTML elements, and not data types.

Hence the ReactElement.isValidElement check fails, and the warning is never shown.

Sign up to request clarification or add additional context in comments.

1 Comment

By my understanding, yes! The function traverseAllChildrenImpl from react-dom knows how to traverse children in a multidimensional array (it recursively calls itself to build the children nodes), but the validateExplicitKey function doesn't do this
1

I've been wondering the same thing recently!

From what I've understood in ReactJS's official docs about how the keys work, I would expect to get the same warning with the nested array of data, just like the one with the flat array of data, since in both cases there are no key attributes set.

Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity.

I actually filled in a bug report (issue) in the ReactJS official GitHub repo, describing the same case you pointed out, but simplified (without the fancy .map() and .reduce() thingies).

It looks like a bug to me.

PS: I will update my answer as soon as the React team responds to me.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.