Ad

How Do I Set The Key In A Manually Created Array In React/JSX?

- 1 answer

I am constructing a toolbar based on a few different states. Everything works fine, I can push in only the required (at the time) buttons and all is dandy.

Because it is an array, React wants its precious key on each element. However manually setting them at each push point seems less than flexible, as I will need to manually update as I add more items.

I tried a simple map post creation (see the _.map line) however item.key is read only.

Is there a way to auto-assign a key based on the array index or similar?

var toolbar = null;
if(!this.state.loading) {
    toolbar = [];
    if(this.state.conditionOne) {
        toolbar.push(<a onClick={this.toggleOne}>Close Section One</a>);
    } else {
        toolbar.push(<a onClick={this.toggleOne}>Show Section One</a>);
    }
    if(this.state.conditionTwo) {
        toolbar.push((
            <div>
                <a onClick={this.otherAction}>Other Action</a>
                <a onClick={this.toggleTwo}>Close Section Two</a>
            </div>
        );
    } else {
        toolbar.push(<a onClick={this.toggleTwo}>Show Section Two</a>);
    }

    _.map(toolbar, (item, i) => { item.key = i; });
}

// Later
return (
    Stuff and things
    {toolbar}
);
Ad

Answer

I am sure you have figured out a nice way by now. As written in the comments yesterday it might probably not be so bad to hand-assign the keys based on the behavior of the buttons.

However, if there are too many of them (or just to keep the creation of a key in one well-defined place) you could centralize the computation. I've setup a small Codepen to illustrate it.

class App extends React.Component { 
  // (1) Helper method
  applyMenuItemProps(label) {
    return {
      label: label,
      key: _.snakeCase(_.escape(label))
    };
  }

  render() {
    // (2) Stateless component
    const MenuItem = (props) => <li><a target="_blank" rel="nofollow noreferrer" target="_blank" rel="nofollow noreferrer" href="#">{ props.label }</a></li>;

    // (3) Using the Spread operator to apply the properties
    let menuItems = [
      <MenuItem {...this.applyMenuItemProps("Hello World")} />,
      <MenuItem {...this.applyMenuItemProps("Ham and Eggs")} />
    ];

    return (
      <div>
        <p><strong>Render with keys</strong></p>
        { menuItems }
      </div>
    );
  }
}

(1) is a helper method that returns the label and a computed key. In this case I am using Lodash's/Underscore's snakeCase and escape to compute a string based on the label (which of course will only work as long as there are not two items with the same label in the same array).

(2) uses React's stateless components (docs), which I started to like a lot. If they should be reusable they can go into a separate module, but one of the things I like about them is that they don't necessarily need a separate file and thus boilerplate can be minimum.

(3) uses Javascript's Spread operator (ES2015) to apply the properties of the object returned by applyMenuItemProps() as attributes on the <MenuItem/> element.

The Codepen also has the onClick handlers applied. I left that out in here because it doesn't have a lot to do with centralizing the computation of the key. Hope it helps and that you enjoy working with React. :)

Ad
source: stackoverflow.com
Ad