简体   繁体   中英

How can I communicate between adjacent elements in ReactJS?

I'm trying to construct a table in ReactJS that generates two rows for each element in an array. The problem I'm having trouble solving is generating them in such a way where row(n) can send a message to row(n+1).

The application of this is opening a detail view if one of the rows is clicked.

Right now my approach is to generate the rows and pass row(n+1) as a prop of row.

const orders = [
  // this is just some example data
  {
   "name": "lorem",
   "number": "20.00",
   "price": "20.00",
   "image": "http://localhost/path/to/image1.jpg"
  },
  {
   "name": "lorem",
   "number": "20.00",
   "price": "20.00",
   "image": "http://localhost/path/to/image1.jpg"
  },
];

const Orders = React.createClass({

  renderAllRows(order) {
    // this function would generate all the rows of the table
    const rows = [];
    orders.map(function (order, index) {
      const OrderDetailInstance = <OrderDetail display={false}  item={order} />
      // OrderDetailInstance is passed as a prop of OrderItemInstance
      const OrderItemInstance = <OrderItem detail={OrderDetailInstance} item={order}/>;
      rows.push(OrderItemInstance, OrderDetailInstance);
    });
    return rows;
  },

  render() {
    const { state } = this;
    const { orders } = state;
    const { isLastPage } = state;

    return (
        <Table>
          <tbody>
            {this.renderAllRows(orders).map(function(row) {
               return row;
            })}
          </tbody>
        </Table>
    );
  },
});

However this doesn't work, because while the prop is successfully passes, I do not know how to access methods on a react element. So I'm obviously going about this wrong.

Currently this is my unsuccessful approach to calling a method on a react element.

const OrderItem = React.createClass({
  render() {
    const item = this.props.item;


    return (
      <tr>
        <td>{item.number}</td>
        <td>{item.number}</td>
        <td>
          <a onClick={this.openOrderDetail}>open detail</a>
        </td>
      </tr>
    );
  },

  openOrderDetail() {
    // This is where I'm trying to call the prop's method.
    this.props.detail.open();
  }
});

const OrderDetail = React.createClass({
  render() {
    const display = this.props.display;
    const classes = display ? classNames('') : classNames('hidden');
    return (
      <tr className={classes}>
        <td colSpan="3">
          <div>
            This is the detail of the previous row
          </div>
        </td>
      </tr>
    );
  },

  open() {
    // This should IDEALLY be exposed to any react element that has
    // OrderDetail as a prop.
    console.log("open");
  }
});

I'm open to the idea of using the state of the Orders class but I can't help feel that would be overkill.

What if you just used CSS to show/not show the detail row unless it was preceded by a row with a certain class.

So, your DOM could look like

<table>
  <!-- Item -->
  <tr class='show-detail'></tr>
  <!-- Detail -->
  <tr class='detail-row'></tr>
</table>

And your CSS could be something like

.detail-row { display: none}
.show-detail + .detail-row { display: block }

So a detail-row is not shown, unless it immediately follows a show-detail item.

Then, your OrderItem component is only worried about toggling a class on itself, rather than talking to it's sibling.

Wrap OrderItem and OrderDetail in a container component, say: OrderView . Keep the state of whether to show the detail or not in the new OrderView . In your OrderView.render() , choose to show the detail row or not based on the state. Instead of trying to call open() , just set the OrderView.state as required.

This tutorial really helped me a lot.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM