Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React refs do not update between render

So I have this component

var LineItemRowsWrapper = React.createClass({
  current_lineitem_count: 0,

  getAjaxData: function(){
    var lineitem_data  = [];
    for(var i = 0; i < this.current_lineitem_count; i++){
        var data = this.refs['lineitem_'+i].getAjaxData();

        lineitem_data.push(data)
    }
    return lineitem_data;
  },

  getLineitems: function(){
    var self = this;

    var lineitem_components = [];
    this.current_lineitem_count = 0;
    if(this.props.shoot){
      var preview = this.props.preview;

      var lineitems = this.props.shoot.get_lineitems();


      lineitem_components = lineitems.map(function (item, index) {
          var ref_str = 'lineitem_'+self.current_lineitem_count;
          self.current_lineitem_count++;

          return (
            <LineItemRow item={item} key={index} ref={ref_str} preview={preview} onChange={self.props.onChange} />
          )
        });
      }

    return lineitem_components;
  },

  render: function() {
    var lineitems = this.getLineitems();
    return (
      <div>
        {lineitems}
      </div>
    )
  }
})

the first time lineitems are rendered the refs work like expected. But if I add a lineitem to this.props.shoot the refs object of this component does not change.

So for example say I had an array of 3 lineitems

 [i1,i2,i3]

this.refs would be

 {lineitem_0:{}, lineitem_1:{}, lineitem_2:{}}

and when I update my lineitem array to be

 [i1,i2,i3,i4]

this.refs does not change, it will still be

 {lineitem_0:{}, lineitem_1:{}, lineitem_2:{}}

why doesn't the refs object update between renders? The LineItemRow components update properly so I know its not something wrong on that front. Any insights would be much appreciated!

____Edit____ (requested to add more code for context)

var DocumentContent = React.createClass({
  contextTypes: {
    router: React.PropTypes.func.isRequired
  },

  getParams: function(){
    return this.context.router.getCurrentParams()
  },

  getInitialState: function() {
    return {
      shoot: ShootStore.get_shoot(this.getParams().shoot_id),
    }
  },

  componentWillMount: function() {  
    ShootStore.bind( 'change', this.onStoreUpdate );
  },

  componentWillUnmount: function() {  
    ShootStore.unbind( 'change', this.onStoreUpdate );
  },

  onStoreUpdate: function(){
    this.setState(this.getInitialState());
  },



  addLineItem: function() {
      ShootActions.create_lineitem(this.state.shoot.id);
  },


  update_shoot_timeout: null,

  update_shoot:function(){
    var self = this;
    window.clearTimeout(this.update_shoot_timeout)
    this.update_shoot_timeout = window.setTimeout(function(){

        var lineitem_data = self.refs.lineitems.getAjaxData()

        if(self.props.shoot){
            ShootActions.update_shoot(self.state.shoot.id, lineitem_data )
        }
    }, 500)
  },


  render: function() {

    var shoot = this.state.shoot;
    return (
        <div className='document__content'>
            <div className='row'>


            <div className='document__expenses'>
                <h3 className='lineitem__title'> Expenses </h3>
                <LineItemRowsWrapper shoot={shoot} onChange={this.update_shoot} ref='lineitems'/>

            </div>
            <button onClick={this.addLineItem} className="btn-small btn-positive">   
                       + Add Expense
                    </button>  




        </div>
    );
  } 
})
like image 473
Charles Haro Avatar asked Mar 31 '15 06:03

Charles Haro


People also ask

Does ref trigger Rerender?

Like state, refs let you retain information between re-renders of a component. Unlike state, setting the ref's current value does not trigger a re-render. Don't read or write ref.

Why ref is not recommended in React?

We should not use ref attribute on function components because they do not have instances. React will assign the current property with Dom element when component mount and assign null to it when component unmount. ref updates happen before componentDidMount or componentDidUpdate methods.

Does updating variable from useRef hook cause component render?

The equivalent ways in functional components using Hooks: In a state variable: useState or useReducer . Updates in state variables will cause a re-render of the component.


1 Answers

Under the section "Caution" in the react documentation about refs https://facebook.github.io/react/docs/more-about-refs.html

"Never access refs inside of any component's render method - or while any component's render method is even running anywhere in the call stack."

Which is exactly what you're doing.

Instead you should store state about the component in this.state or properties of the component in this.props

like image 162
jeff_kile Avatar answered Oct 01 '22 03:10

jeff_kile