render in a different context in react?

1

I'm doing a project on react + meteor.

The problem is this:

I have an object that renders two columns. In the first column (col-lg-4) there is a list of elements. In the second column (col-lg-8) I want to show a graph that must be shown when pressing each of the elements of the first column. The problem is that the click event of each of the elements ( StockSingle ) I have already overwritten with the data load for each of the graphics.

This is the object App :

export default class App extends TrackerReact(Component){

  render(){
    <div className="col-lg-4">
      <ul className="stocks">
        {this.stocks().map(function(stock){
          return(<StockSingle key ={stock._id} stock={stock}/>)})}
      </ul>
   </div>
  <div className="col-lg-8">
   <myChart/>
  </div>
  }
}

This is the object StockSingle :

export default class StockSingle extends Component{

  constructor(props){
    super(props);
    this.state={
      showComponent:false
    }
  }

  clickStock(event){
    this.setState({
      showComponent:true
    });
  }

  render() {
    return (
      <div>
      <li onClick={this.clickStock.bind(this)}>
      {this.props.stock.name}
      </li>
      {this.state.showComponent?<Loader stock={this.props.stock}/>:null}
      </div>
    )
  }
}

The loader object loads the data and shows a gif spinner while it is working:

export default class Loader extends Component {

  componentDidMount(){
    this.getInfo();
  }
  getInfo(){
    //loads the data here...
  }

  render(){
    return(
      <div className="loading">
        <img src="loader2.gif" width='20' height='20' />
      </div>
   )
  }
}

I can not figure out how to render the graph in col-lg-8 by clicking on each of the StockSingle elements. Maybe reactivevars of meteor?

Any help is appreciated.

    
asked by sub 12.04.2017 в 20:49
source

1 answer

1

What you are trying to do is sibling communication. There are several options:

  • Use context to make properties available in a hierarchy (not recommended).
  • Use the observer / pub-sub pattern. There are libraries like EventEmitter or Postal that implement this pattern (recommended).
  • Pass shared methods as property (recommended).
  • Use redux / reflux. The latter option is feasible only in medium / large or high complexity applications.

The simplest alternative you have is to define methods in the parent component, methods that will be shared in the children.

  

Tip: Try not to bin a context directly in the HTML, since each time the component is rendered it will create a new instance of the function which is not very efficient. Instead, make the bind in the constructor or use the decoration @autobind of core-decorators .

App

class App extends React.Component {

  constructor(props) {
    this.state = {
      stock: {} // elemento activo
    };
  }

  render() {
    return (
      <div className="col-lg-4">
        <ul className="stocks">
          {
            this.stocks().map(stock => (
              <StockSingle 
                key ={stock._id} 
                stock={stock}
                notify={this.onStockClick.bind(null, stock)}
              />
            ));
          }
        </ul>
      </div>
      <div className="col-lg-8">
       <myChart 
         stock={this.state.stock}
       />
      </div>
    );
  }

  /**
   * Será llamado por StockSingle cada vez
   * que se un stock sea pulsado.
   * @param  {SingleStock} stock Stock pulsado
   */
  onStockClick(stock) {
    this.setState({ stock });
  }
}

StockSingle

export default class StockSingle extends Component {

  constructor(props) {
    super(props);
    this.state={
      showComponent:false
    };
  }

  clickStock(event) {
    this.setState({
      showComponent:true
    });
    // notificamos que se ha pulsado sobre un stock
    this.notify(this.props.stock);
  }

  render() {
    return (
      <div>
        <li onClick={this.clickStock.bind(this)}>
        {this.props.stock.name}
        </li>
        {
          this.state.showComponent 
          ? <Loader stock={this.props.stock}/>
          :null
        }
      </div>
    );
  }
}

Chart

export default class Chart extends Component {

  constructor(props) {
    super(props);
    this.state = {
      stock: {}
    };
  }
  /*
   * Este método será llamado cuando se reciba
   * una propiedad desde fuera. Si la propiedad 
   * es otra, actualizamos el estado, causando
   * una re-renderización.
   */
  componentWillReceiveProps (nextProps) {
    const { stock } = nextProps;
    if (this.state.stock._id && stock._id !== this.state.stock._id) {
      this.setState({ stock });
    }
  }

  render() {
    return (
      <!-- HTML here -->
    );
  }
}

Basically three things happen:

  • Element% co_of% pressed. Update your status and call SingleStock .
  • Method notify of notify executed. Update the status by changing the stock .
  • Graph detects a change in its properties. Compare the stock property with the stock in your state. If they are different, it is re-rendered.
  • Redux

    With Redux it's much simpler, for example, we only dispatch one action and the reducer will update the status. When you click on a App we dispatch an action like this:

    const changeStock = stock => {
      return {
        type: 'CHANGE_STOCK',
        stock
      };
    }
    

    In this way ( StockSingle ):

    this.props.dispatch(changeStock(this.state.stock));
    

    Then, by means of a reducer:

    const stockReducer = (state = {}, action) => {
      switch (action.type) {
        case 'CHANGE_STOCK':
          return Object.assign({}, state, { action.stock });
        default: return state;
      }
    };
    

    We update the status (called store , which represents the state of the application, not local) overwriting the stock clicked. Finally, we get it in SingleStock :

    @connect(
      state => { stock: state.stock }
    )
    class Graph extends Component {
    
      constructor (props) {
        this.state = {
          stock: {}
        };
      }
    
      componentWillReceiveProps (nextProps) {
        const { stock } = nextProps;
        if (this.state.stock._id && this.sate.stock._id !== stock._id) {
          this.setState({ stock });
        }
      }
    
      render() {
        return (
          <!-- HTML aquí -->
        );
      }
    }
    

    The decoration Graph connects the state of the application ( store ) with that component. The first parameter indicates what data from the store we want to be available in @connect .

    Redux may seem complicated at first, if you are starting with React I recommend you go with the first approach.

        
    answered by 12.04.2017 / 21:38
    source