Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React - Decorating Children and Handling Keys

Tags:

reactjs

I'm writing a small component that decorates some children. Given the following sample:

const Child = props => <div>{props.name}</div>;
const Parent = props => {
  let wrappedChildren = React.Children.map(props.children, c => {
     return (<Decorator key={c.key}>
       {c}
     </Decorator>);
  });

  return (<div>
    {wrappedChildren}
  </div>);
}
const Consumer = props => {
     let children = [0, 1, 2].map(num => {
       return <Child key={num} name={num} />;
     });
    return <Parent>{children}</Parent>;
});

In this code, I'm wanting to take each child and decorate it with some wrapping container or some behaviour. Forgetting for the moment that there may only be one child, I need to give each instance a key.

Currently I'm assuming that each child does have a key which isn't fantastic, lifting it off the child and applying it to the Decorator directly.

Is this the "correct" way of doing this? Is there a better way?

like image 528
Ray Booysen Avatar asked Dec 09 '25 10:12

Ray Booysen


2 Answers

I think your approach is fine. And you do need the key on the top level. Use the child's key, if it is there. If not, fall back to the index, as React recommends:

When you don't have stable IDs for rendered items, you may use the item index as a key as a last resort.

Be advised though:

We don't recommend using indexes for keys if the items can reorder, as that would be slow.

Source: React Docs about keys

const Child = props => <div>{props.name}</div>;
const Parent = props => {
  let wrappedChildren = React.Children.map(props.children, (c, i) => {
    const key = c.key ? `key-${c.key}` : `index-${i}`
    return (
      <Decorator key={key}>
        {c}
      </Decorator>
    );
  });

  return (
    <div>
      {wrappedChildren}
    </div>
  );
};
const Consumer = () => {
  let children = [ 0, 1, 2 ].map(num => {
    return <Child key={num} name={num} />;
  });
  return <Parent>{children}</Parent>;
};
like image 132
MoeSattler Avatar answered Dec 11 '25 01:12

MoeSattler


That would work with the current version of React, 15.6.1 (and probably with prior versions as well). However, there is a slightly better way to achieve your goal with a small tweak, which would be delegating the lifting on a prop, rather than using directly the key.

The reason is that the concept of a key is something that is controlled by React internals before your component gets created, so it falls into the implementation details category, with the consequence that future versions of React could break your code without you even noticing it.

Instead, you can use a prop on your Child component, for instance id, based on your assumption that each child does have some sort of unique value. Your code would result in:

const Child = props => <div>{props.name}</div>;

const Parent = props => {
  return React.Children.map(props.children, c => {
     return (<Decorator key={c.props.id}>
       {c}
     </Decorator>);
  });

  return (<div>
    {wrappedChildren}
  </div>);
}

const Consumer = props => {
     let children = [0, 1, 2].map(num => {
       return <Child id={num} name={num} />;
     });
    return <Parent>{children}</Parent>;
});

If you have a more complex structure and want to avoid passing props individually, the parent component should held the responsibility of generating unique ids for the children. You can follow the ideas explained in this answer https://stackoverflow.com/a/29466744/4642844.

Those are the steps I'd follow:

  • Implement your own utility function or use an existing browser library to generate unique ids.
  • Implement componentWillMount in your Parent component to create an array of unique ids. You can use an internal member class variable instead of local state. It would be something like:

    componentWillMount() {
      this.uuids = React.Children.map(() => uuid());  
    }
    
  • Inside your Parent render, assign each key to the Decorator component in the following way.

     render() {
       return React.Children.map(props.children, (c, i) => {
         return (<Decorator key={this.uuids[i]}>
           {c}
         </Decorator>);
       });
     }
    

Some useful links:

http://mxstbr.blog/2017/02/react-children-deepdive/

https://medium.com/@dan_abramov/react-components-elements-and-instances-90800811f8ca

like image 31
rgommezz Avatar answered Dec 11 '25 01:12

rgommezz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!