V.Tur
V.Tur

Reputation: 1427

ReactJS. How to get states of elements inside react component

Help me please to get states of elements inside react component or other some custom features with information which I need. Every of td tags contains react component Block1. Simplified code structure below

class Block2 extends React.Component {
render() {
  return (
    <table>
      <tbody>
        <tr>
          <td>
            <Block1 />
          </td>
          <td>
            <Block1 />
          </td>
        </tr>
      </tbody>
    </table>
  )}}

Block1 - react component which contain div elements. Block2 is inside component Block3. How to get states of Block1 from Block3 by click on some button? Now, I can get list of Block1 and may see props, but I can't see states. Or I can get DOM td elements and see children classNames (which I looking for in states) but I can't see props...

Upvotes: 1

Views: 5726

Answers (1)

Dane
Dane

Reputation: 9582

Unless you use libraries like Redux, you have to do the following to solve your problem:
Store the state inside Block3 and not Block1. Then pass any function that changes this state as props from Block3 to Block2 to Block1. When any change occurs in Block1, call this function. The pattern should be:

class Block3 {
  changeState(value) {
    this.setState({ stateValue: value });
  }
  render() {
    return (
      <Block2 changeState={this.changeState}/>
    )
  }
}

class Block2 extends React.Component {
render() {
  return (
    <table>
      <tbody>
        <tr>
          <td>
            <Block1 changeState={this.props.changeState} />
          </td>
          <td>
            <Block1 changeState={this.props.changeState} />
          </td>
        </tr>
      </tbody>
    </table>
  )}}


class Block1 {
  changeHandler(ev) {
    this.props.changeState(ev.target.value);
  }
  render() {
    return (
      <button onClick={this.changeHandler}/>
    )
  }
}

If you really want to access the child state inside a parent component, consider using refs:

class Block2 {
  render() {
    return <Block1 ref={ (childComponent) => { this.block1Child = childComponent; } } />;
    // Now you may use this.block1Child to access child component's state eg: this.block1Child.setState({})
  }
}

EDIT: My solution after seeing your code:

import React, { Component } from 'react';
import './Hall.css';

class HallCol extends Component {
    constructor(props) {
      super(props);
      this.state = {
        chair_cls:'chair-default'
      };
      this.handleClick = this.handleClick.bind(this);
    }

    handleClick(e) {
      let chair_cls = null;
      if(this.state.chair_cls==="chair-default") {
        chair_cls = 'chair-sold';
        this.props.updateCount(1, this.props.seat_number);
      } else {
        chair_cls = 'chair-default';
        this.props.updateCount(-1, this.props.seat_number);
      }

      this.setState({
        chair_cls: chair_cls
      });
    }

    render(){
      return(
            <div className={this.state.chair_cls} onClick={this.handleClick}>
              <div className="backrest">
                  <p>{this.props.seat_number}</p>
              </div>
              <div className="armrest-left">
              </div>
              <div className="armrest-right">
              </div>
            </div>
        );
    }
}

class Table extends React.Component {
  constructor() {
    super();
    this.genRow = this.genRow.bind(this);         // this is method binding
  }

  genRow() {
    var rows = this.props.rows;
    return rows.map(function(v, i) {
      var tmp = v.map(function(v2, j) {
        return (
          <td key={'td' + i + '_' + j} className='chair-cell' >
            {v2}
          </td>
        );
      });

      return (
        <tr key={'tr' + i}>
          {tmp}
        </tr>
      )
    });
  }

  render() {
    return (
      <table className="hall-grid" >
        <tbody>
          {this.genRow()}
        </tbody>
      </table>
    );
  }
}

class Hall extends React.Component {
  constructor(props) {
    super(props);
    var rows_num = this.props.rows;
    var cols_num = this.props.cols;

    this.AddRow = this.AddRow.bind(this);
    this.updateSeats = this.updateSeats.bind(this);

    var row = [];
    for (var i = 0; i < rows_num; i++) {
      var col = [];
      for (var k = 0; k< cols_num; k++) {
          col.push(<HallCol row = {i+1} seat_number = {k+1} updateCount={this.updateSeats} />);  // bind the passed function to parent
      }
      row.push(col);
    }
    this.state = {
      rows: row,
      bookedSeats: [],
      count: 0
    };
  }

  AddRow() {
    let newRows = this.state.rows;
    newRows.push([0, 0]);
    this.setState({rows: newRows});
  }
  updateSeats(val, seat_number) {
    let bookedSeatsUpdated;
    if( val === 1 ) {
      bookedSeatsUpdated = this.state.bookedSeats.concat(seat_number);
    } else {
      bookedSeatsUpdated = this.state.bookedSeats;
      let index = bookedSeatsUpdated.indexOf(seat_number);
      bookedSeatsUpdated.splice(index,1);
    }
    this.setState({
      bookedSeats: bookedSeatsUpdated,
      count: this.state.count + val
    });
  }

  render() {
    return (
      <div className="hall">
        <Table rows={this.state.rows} />
        <button className = "btn-default" onClick={() => {
            alert(this.state.count + 'seats : ' + this.state.bookedSeats);
          } }>
            TOTAL SOLD
        </button>
      </div>
    );
  }
}

export default Hall;

Upvotes: 1

Related Questions