Ad

Communicate With Parent Component Without Using State/props

- 1 answer

I need a parent component to know when it's child components have changed from collapsed to expanded and viceversa.

I don't want to keep that in the app state (Redux). I can't move the logic up to the parent component.

My components are:

  • I have component C which is an item that can be expanded/collapsed.
  • Then I have Component B which is a wrapper for component C in a specific case (gives it the draggable behaviour).
  • And finally component A which lists components C in a loop. A, sometimes wraps Cs in B, and sometimes not (when items shouldnt be draggable).

I need B to know C is expanded, so that it isn't draggable while expanded.

I can't put the expanded/collapsed logic in B, because C should always be collapsible/expandable independently of if draggable.

Is there any simple way to accomplish this without needing to have the expand/collapse state of each item in the app state?

I have read about https://facebook.github.io/react/docs/context.html but seems still in experimental phase...

Ad

Answer

You can keep the expanded state inside the C component and update the draggable state of the B component with a callback passed with the props of the C component. In this way both components keep their own state, no need to add the state on the A component or in you App state (the Redux one).

Here is an example: https://jsfiddle.net/snahedis/69z2wepo/28567/

var sampleList = [{id: 1, draggable: true},{id: 2, draggable: false}];

var Acomponent = React.createClass({
  render: function() {
    return (
      <div>
        {sampleList.map(function(component) {
          if (component.draggable) {
            return <Bcomponent key={component.id} />;
          } else {
            return <Ccomponent key={component.id} />;
          }
        })}
      </div>
    );
  }
});

var Bcomponent = React.createClass({
  getInitialState: function() {
    return {
        draggable: true
    }
  },
  hasBeenToggled: function() {
    this.setState({
        draggable: !this.state.draggable
    });
  },
  render: function() {
    return <Ccomponent draggable={this.state.draggable} toggleExpandableCallback={this.hasBeenToggled} />;
  }
});

var Ccomponent = React.createClass({
  getInitialState: function() {
    return {
        expanded: false
    }
  },
  toggleExpandable: function() {
    this.setState({
        expanded: !this.state.expanded
    });

    if (typeof this.props.toggleExpandableCallback === "function") {
        this.props.toggleExpandableCallback();
    }
  },
  render: function() {
    return (
      <div>
        <div>I'm the C component and my expanded state is : {this.state.expanded.toString()}</div>
        <div>{(this.props.draggable) ? "oh, and I'm draggable !" : "and I'm saddly not draggable"}</dgt;
        <div>{(this.props.draggable) ? "oh, and I'm draggable !" : "and I'm saddly not draggable"}</div>
        <button onClick={this.toggleExpandable}>Change expandable state</button>
      </div>
    );
  }
});

ReactDOM.render(
  <Acomponent />,
  document.getElementById('container')
);
Ad
source: stackoverflow.com
Ad