Component state delegation with React I'm currently reviewing some different ways to have reusable components enforcing both most of the style &amp; logic. The use case is a generic MainList with children components (eg. MainListToolbar, MainListContent, etc.) that can all be used together to create multiple mostly agnostic lists for business objects (eg. UserList, DeviceList, etc.).

For instance, I want to abstract away a multi-selectable logic and design in MainListContent while still being able to access the related internal state in the parent UserList (eg. this.state.selectedItems). Ideally, the multi-selectable logic (eg. onListItemClick, onListItemDoubleClick, etc.) should not leak at all into UserList.

I first thought about extending some MainListComponent (instead of Component), but I've never seen it in the React world and looks like a bad pattern to me.

I got something working with a state delegation pattern that enables to have an UserList that implements generic children elements and inherits the abstracted logic (eg. listItem selection) in it's own state to use it any way it wants.

Is there something flawed in this pattern/code? Is there a more elegant/safe way to achieve that?   



const {Component} = React;

class MainList extends Component {
 render() {
    const {children} = this.props;
   return (
     &lt;div&gt;{children}&lt;/div&gt;
    );
  }
}

class MainListContent extends Component {
  constructor(props) {
   super(props);
    this.state = {
     selectedItems: []
    };
   this.handleListItemClick = (ev) =&gt; {
     const {selectedItems} = this.state;
     const value = ev.target.innerHTML;
      this.setState({
       selectedItems: !selectedItems.includes(value) ? selectedItems.concat(value) : selectedItems.filter(item =&gt; item !== value)
      });
    }
  }
  setState(nextState) {
    super.setState(nextState);
    const {onSetState} = this.props;
    if (onSetState) {
      onSetState(nextState);
    }
  }
  render() {
    const {title, items} = this.props;
    const {selectedItems} = this.state;
   return (
     &lt;div&gt;
       &lt;h5&gt;{title}&lt;/h5&gt;
        &lt;ul&gt;
          {items.map((item, index) =&gt; (&lt;li key={index} onClick={this.handleListItemClick} style={{cursor: 'pointer', fontWeight: selectedItems.includes(item) ? 'bold' : 'normal'}}&gt;{item}&lt;/li&gt;))}
        &lt;/ul&gt;
      &lt;/div&gt;
    );
  }
}

class UserList extends Component {
  constructor(props) {
   super(props);
    this.state = {};
    this.delegateSetState = this.setState.bind(this);
  }
  render() {
    const {items} = this.props;
    const {selectedItems} = this.state;
   return (
      &lt;MainList&gt;
        &lt;MainListContent ref={ref =&gt; { this.mainListContent = ref; }} onSetState={this.delegateSetState} title="Users" items={items} /&gt;
        &lt;pre&gt;count: {selectedItems ? selectedItems.length : 0}&lt;/pre&gt;
      &lt;/MainList&gt;
    )
  }
}

const Root = props =&gt; (
  &lt;UserList items={['foo', 'bar', 'baz']}&gt;&lt;/UserList&gt;
);

ReactDOM.render(
  &lt;Root /&gt;,
  document.getElementById('root')
);
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"&gt;&lt;/script&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"&gt;&lt;/script&gt;
&lt;div id="root"&gt;&lt;/div&gt;
One prevailing pattern is stateless components. You will most likely have components that require state, but it is easier to follow the flow of data (and write tests) if your components rely solely on props. The stateless pattern is somewhat similar to yours (in that they are both mutating state in the parent), but instead of proxying calls to setState on the top level component AND having to re-implement setState in each component, child components just call the methods passed from the top level to mutate the state and then receive the new data in the form of props.

From React Docs:


  In an ideal world, most of your components would be stateless
  functions because in the future we’ll also be able to make performance
  optimizations specific to these components by avoiding unnecessary
  checks and memory allocations. This is the recommended pattern, when
  possible.


Here is your example using stateless components (except for the top level component):



const { Component } = React;

const MainList = (props) =&gt; {
   const {children} = props;
   return (
     &lt;div&gt;{children}&lt;/div&gt;
   );
}

const MainListContent = (props) =&gt; {
   const {title, items} = props;
   return (
      &lt;div&gt;
       &lt;h5&gt;{title}&lt;/h5&gt;
        &lt;ul&gt;{
            items.map((item, index) =&gt; (
               &lt;li key={index} 
                   onClick={(e) =&gt; props.onSelectItem(item.name)} 
                   style={{cursor: 'pointer', fontWeight: item.selected ? 'bold' : 'normal'}}
               &gt;{item.name}&lt;/li&gt;
              ))
        }&lt;/ul&gt;
      &lt;/div&gt;
   );
}

class UserList extends Component {
  constructor(props) {
    super(props);
    this.state = {
        items: props.items.map(item =&gt; ({
            selected: false,
            name: item
        }))
    }
  }
  onSelectItem(itemName) {
    let { items } = this.state;
    this.setState({
        items: items.map(item =&gt; {
            if (item.name === itemName) {
                item.selected = !item.selected;
            }
            return item;
        })
    });
  }
  render() {
    const { items } = this.state;
   return (
      &lt;MainList&gt;
        &lt;MainListContent ref={ref =&gt; { this.mainListContent = ref; }} onSelectItem={this.onSelectItem.bind(this)} title="Users" items={items} /&gt;
        &lt;pre&gt;count: {items.filter(item =&gt; item.selected).length}&lt;/pre&gt;
      &lt;/MainList&gt;
    )
  }
}

const Root = props =&gt; (
  &lt;UserList items={['foo', 'bar', 'baz']}&gt;&lt;/UserList&gt;
);

ReactDOM.render(
  &lt;Root /&gt;,
  document.getElementById('root')
);
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"&gt;&lt;/script&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"&gt;&lt;/script&gt;
&lt;div id="root"&gt;&lt;/div&gt;