HI
I have a ListView and need to measure the layout of one row. I searched, but did not find a way to access a ListViews rows. ( ListView._visibleRows seems to be an empty object ).
Is there a way to do this?
If not I'd suggest to add a "ref" prop, based on the sectionID and rowID to every
+1
One could just add a simple function that writes the ref of each list element to the _visibleRows property.
Proposal: ( part of the ListView's render function )
for (var rowIdx = 0; rowIdx < rowIDs.length; rowIdx++) {
var rowID = rowIDs[rowIdx];
var comboID = sectionID + rowID;
var shouldUpdateRow = rowCount >= this.state.prevRenderedRowsCount &&
dataSource.rowShouldUpdate(sectionIdx, rowIdx);
var row =
<StaticRenderer
key={'r_' + comboID}
ref={ function(rowRef) { this._visibleRows[comboID] = rowRef; } }
shouldUpdate={!!shouldUpdateRow}
render={this.props.renderRow.bind(
null,
dataSource.getRowData(sectionIdx, rowIdx),
sectionID,
rowID
)}
/>;
bodyComponents.push(row);
totalIndex++;
if (++rowCount === this.state.curRenderedRowsCount) {
break;
}
}
+1
You should be able to put a function ref in renderRow:
render: function() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData) =>
<Text ref={(row, sec, i) => this.rows[sec][i] = row}>{rowData}</Text>
}
/>
);
},
or similar.
@spicyj Thanks you directed me on the right path :+1:
This worked for me:
render: function() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData, sec, i) =>
<Text ref={(row) => this.rows[sec][i] = row}>{rowData}</Text>
}
/>
);
},
Hey @lukasreichart getting stuck on this. How do you access the row once you set the ref. this.rows and this.props.rows, and this.props.refs all return undefined
@lukasreichart thanks to your code 馃憤
@arilitan you can access it with this.rows[sectionId][rowId] beacuse it's an array.
Or you can change
<Text ref={(row) => this.rows[sec][i] = row}>{rowData}</Text>
to
<Text ref={(row) => this.rows[i] = row}>{rowData}</Text>
and access it via this.rows[rowid].
I am getting an error
undefined is not an object (evaluating '_this2.rows')
<View ref={(rowData, sectionID, rowID) => this.rows[sectionID][rowID] = row} >
HI @senthilsivanath . I'm running in to the same issue, did you have any luck in resolving?
Hi @brianfoody and @senthilsivanath I am also running into the same issue. Were you able to resolve it?
No luck since @samdturner
@spicyj @lukasreichart - the solution with refs does not work for me all the time - specifically for "long" lists where children are not yet rendered (due to ListView optimalization). Is there a better way then setting high value of scrollRenderAheadDistance?
Sometimes a null gets supplied to a callback ref={r => // r is null}. So, I had to do something like ref={r => r && (rowsArray[rowId] = r) }, so that rows are not assigned null when that happens.
@sodik82 take a look on https://github.com/sghiassy/react-native-sglistview
After trying the REFS way and it being not a very good approach, I noticed the onChangedVisibleRows params ( visibleRows and changedRows ) are hashes. They must have done this for a reason. It then made sense to assign the row rendered its sectionId and rowId and also an event emitter defined from the parent. The parent then does an emit change event when onChangedVisibleRows occurs, passing in visibleRows and changedRows. Since each row has props defined for sectionId and rowId, each row can then do a quick lookup in the hashes to see if its visible or not.
I have gone through all the solutions but the way i found working is this:
constructor(props) {
super(props);
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
this.data = [
{
id: 1,
title: 'Apple'
}, {
id: 2,
title: 'Samsung'
}
];
this.rows = [];
this.state = {
selectedIndex: 0,
dataSource: ds.cloneWithRows(this.data)
};
}
render() {
return (
<View style={style.container}>
{this.listView()}
</View>
);
}
listView = () => {
return (<ListView
ref={(instance) => this.list = instance}
dataSource={this.state.dataSource}
renderRow={this.renderRow}/>);
}
renderRow = (rowData, sectionID, rowID) => {
return (<MenuSliderRow
ref={(instance) => this.rows.push(instance)}
key={rowID}
selected={(this.state.selectedIndex == rowID)}
title={rowData.title}
onPress={() => this.onRowSelected(rowID)}/>);
}
Later you can access this like
onRowSelected(rowID) {
this.rows.map((row) => {
row.setSelected(false);
})
const selectedRow = this.rows[rowID];
selectedRow.setSelected(true);
const rowData = this.data[rowID];
this.setState({selectedIndex: rowID})
}
Most helpful comment
You should be able to put a function ref in renderRow:
or similar.