7

Working with a ListView in React-Native, I have seen that is not the same, moving props to the list item,

  1. Pass functions as props only with the reference, and invoke the parameters in the child component, or

  2. Pass functions as props with parameters defined, and invoke the function with no parameters in the child

None of the solutions works.

The function invoked are Actions creators of Redux, and dispatched. Is this a issue of Redux or React-Native (maybe ReactJS)

This is a snippet, market as //ERROR the code lines that does'nt work followed by the good ones

class App extends Component {

  // On props 
  // data: an Array
  // doThis: an action creator of Redux
  // doThat: idem

  constructor(){
    super();
    this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
  }

  render () {
    const dataSource = this.ds.cloneWithRows(this.props.data);

    return (
      <View>
        <ListView style={{flex:1}}
            dataSource={dataSource}
            renderRow={(rowData, sectionID, rowID) =>
              <Item  rowData={rowData}
              //ERROR
              //onPress={this.props.doThis}
              //onLongPress={this..props.doThat}
              //RIGHT NO ERROR TOO
              onPress={() => this.props.doThis(rowData)}
              onLongPress={() => this.props.doThat(rowData)}
              />
            }
          />
      </View>
    )
  }
}

class Item extends Component {
  render() {
    return (
      <View>
        <TouchableHighlight
          //ERROR 
          //onPress={() => { this.props.onPress( this.props.rowData ) }}
          //onLongPress={() => { this.props.onLongPress( this.props.rowData ) }}
          //WRONG TOO
          onPress={this.props.onPress}
          onLongPress={this.props.onLongPress}
          >
          <Text>
            {rowData}
          </Text>
        </TouchableHighlight>
      </View>
    );
  }
}

There is a repo with this problem here https://github.com/srlopez/test Thanks in advance

Santi
  • 491
  • 6
  • 14

2 Answers2

4

If your high-level callbacks accept a parameter, you need to make sure your anonymous functions accept a parameter as well (Note: creating anonymous functions using the arrow syntax automatically binds our function to the value of this in the current context). I think you witnessed a combination of issues where either your callbacks were bound to the incorrect context (the window) or you weren't accepting the passed arguments:

class App extends Component {

  // On props 
  // data: an Array
  // doThis: an action creator of Redux
  // doThat: idem

  constructor(){
    super();
    this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
  }

  render () {
    const dataSource = this.ds.cloneWithRows(this.props.data);

    return (
      <View>
        <ListView style={{flex:1}}
            dataSource={dataSource}
            renderRow={(rowData, sectionID, rowID) => 
              <Item rowData={rowData}
                  onPress={(data) => this.props.doThis(data)}
                  onLongPress={(data) => this.props.doThat(data)} />
            }/>
      </View>
    )
  }
}

class Item extends Component {
  render() {
    return (
      <View>
        <TouchableHighlight
          onPress={() => this.props.onPress(this.rowData)}
          onLongPress={() => this.props.onLongPress(this.rowData)}>
          <Text>
            {rowData}
          </Text>
        </TouchableHighlight>
      </View>
    );
  }
}
Calvin Belden
  • 3,114
  • 1
  • 19
  • 21
  • Thanks @calvin-belden, I try your approch on this [repo](https://github.com/srlopez/test), but the result behavior is the same. I coded 'onPress={(rowID) => {this.props.update(rowID)}}` on renderRow in ListView and `onPress={()=>this.props.onPress(rowID)}` on Item, and Item change but the remains items are deleted. – Santi Mar 10 '16 at 16:26
  • Is rowID defined in the Item component's `render` function? Can you set a breakpoint (or somehow debug) to see if the parent handler gets called? – Calvin Belden Mar 10 '16 at 16:32
  • 1
    Hello again @calvin-belden @moti-azu. Done! The Right way is like this `onPress={() => this.props.update(parseInt(rowID))}` on renderRow, and `onPress={this.props.onPress}` on Item. But the important thing is the `parseInt` of `rowID` in the repo. The rowID is a string and not a number!. Then the function in the reducer(Redux) always convert to 0, and then all wrong! Thanks too much both! – Santi Mar 10 '16 at 18:09
  • I'm curious if there's another way besides for arrow notation for this? So instead of `onPress={(data) => this.props.doThis(data)` what is the equivalent of something like `onPress={this.props.doThis.bind(this, data)`? Or something similar? – txizzle Jul 19 '16 at 16:04
  • You wouldn't bind the data parameter, as this isn't known at the time of binding; the `data` is passed to your event listener when the event is fired. But other than that [functionally] there is no difference. There are performance implications when using Function.prototype.bind, and I also find the arrow syntax more concise. – Calvin Belden Jul 19 '16 at 16:08
  • So elegant & simple! You are my Saviour! Thanks – Sound Blaster Feb 06 '17 at 00:45
0

It's probably be a problem with your this not being set right in your closure.

Try binding it this way:

class Item extends Component {
  render() {
    return (
      <View>
        <TouchableHighlight
          onPress={this.props.onPress.bind(this, this.props.rowData)}
          onLongPress={this.props.onLongPress.bind(this, this.props.rowData)}
          >
          <Text>
            {rowData}
          </Text>
        </TouchableHighlight>
      </View>
    );
  }
}
Moti Azu
  • 5,392
  • 1
  • 23
  • 32
  • Hi @Moti Azu, Really I don't know whats is wrong with my answer and yours. Both wrong. I was thinking the right way passing the callbak on renderRow and on Item just invoke the function. But doesn't work. this is the repo to test, i you can help me: [https://github.com/srlopez/test](https://github.com/srlopez/test) – Santi Mar 10 '16 at 14:20
  • Hi @Moti Azu to avoid extended discussions here (don`t like reviewers) i removed my comments. The long explanation is on the repo. Clicking on buttons on main component works fine(add, remove, update, etc on the list) but click on items on List, doesn't make the same behavior, and then remove all the items from the clicked one to the end. The desired behavior is to change the status of a item, but the status change and remove the remaining items. Sorry Moti for my awful english. – Santi Mar 10 '16 at 15:33