Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get refs to ListView rows #897

Closed
lukasredev opened this issue Apr 17, 2015 · 16 comments
Closed

Get refs to ListView rows #897

lukasredev opened this issue Apr 17, 2015 · 16 comments
Labels
Resolution: Locked This issue was locked by the bot.

Comments

@lukasredev
Copy link

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 element created in, ListView.render ...

@liubko
Copy link
Contributor

liubko commented May 6, 2015

+1

@lukasredev
Copy link
Author

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;
        }
      }

@deepak2510
Copy link

+1

@sophiebits
Copy link
Contributor

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.

@lukasredev
Copy link
Author

@spicyj Thanks you directed me on the right path 👍

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>
      }
    />
  );
},

@arilitan
Copy link

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

@GilangJulianS
Copy link

@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].

@senthilsivanath
Copy link

I am getting an error

undefined is not an object (evaluating '_this2.rows')

<View ref={(rowData, sectionID, rowID) => this.rows[sectionID][rowID] = row} >

@brianfoody
Copy link

HI @senthilsivanath . I'm running in to the same issue, did you have any luck in resolving?

@samdturner
Copy link

Hi @brianfoody and @senthilsivanath I am also running into the same issue. Were you able to resolve it?

@brianfoody
Copy link

No luck since @samdturner

@sodik82
Copy link

sodik82 commented Jun 20, 2016

@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?

@cyrbon
Copy link

cyrbon commented Jun 25, 2016

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.

@sibelius
Copy link

sibelius commented Aug 3, 2016

@sebringj
Copy link

sebringj commented Jan 25, 2017

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.

@gurmundi7
Copy link

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})
    }


@facebook facebook locked as resolved and limited conversation to collaborators May 29, 2018
@react-native-bot react-native-bot added the Resolution: Locked This issue was locked by the bot. label Jul 22, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Resolution: Locked This issue was locked by the bot.
Projects
None yet
Development

No branches or pull requests