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 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.
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With