React Native— onPress extract id from “currentTarget”

社会主义新天地 提交于 2019-12-07 02:39:27

A better way (avoiding event callbacks creation at every render)
to get the current pressed element properties (id in this example)
is by wrapping it in a parent component, passing data
and binding all events only once (in constructor)

  1. First declare your component wrapper:
    It needs to be a class and not a stateless functional component otherwise you can't avoid the callback creation at every render

    to see the difference, here's a more advanced example in action:
    https://snack.expo.io/ByTEKgEsZ (example source code)

class TouchableText extends React.PureComponent {
  constructor(props) {
    super(props);
    this.textPressed = this.textPressed.bind(this);
  }

  textPressed(){
    this.props.onPressItem(this.props.id);
  }

  render() {
    return (
      <Text style={styles.item} onPress={this._onPress}>
        {this.props.children}
      </Text>
    );
  }
}

If you use a const JSX object (stateless functional component), it works but it's not optimal, the event callback will be created every time the component is rendered as the arrow function is actually a shortcut to the render function

const TouchableText = props => {
  const textPressed = () => {
    props.onPressItem(props.id);
  };
  return <Text onPress={textPressed} />;
};
  1. Then use this wrapper instead of your component as following:
class Test extends React.Component {
  constructor(props) {
    super(props);
    //event binding in constructor for performance (happens only once)
    //see facebook advice: 
    //https://facebook.github.io/react/docs/handling-events.html
    this.handlePress = this.handlePress.bind(this);
  }

  handlePress(id) {
    //Do what you want with the id
  }

  render() {
    return (
      <View>
        <FlatList
          data={ar}
          renderItem={({ item }) => (
            <TouchableText
              id={item.id}
              onPressItem={this.handlePress}
            >
              {`Component with id ${item.id}`}
            </TouchableText>
          )}
        />
      </View>
    );
  }
}

As written in React Native Handling Events Doc, there's another possible syntax to avoid binding in the constructor (experimental though: i.e. which may or may not be supported in the future!):

If calling bind annoys you, there are two ways you can get around this. If you are using the experimental property initializer syntax, you can use property initializers to correctly bind callbacks

this means we could only write

handlePress = (id) => {
    //`this` is already bound!
}

instead of

constructor(props) {
    super(props);
    //manually bind `this` in the constructor
    this.handlePress = this.handlePress.bind(this);
}

+

handlePress(id) {

}

Some references:
Event handlers and Functional Stateless Components
React Binding Patterns: 5 Approaches for Handling this

alexandros84

After 8 hours of searching I ve found the solution on my own, thanks to the fantastic calculator tutorial of Kyle Banks.

https://kylewbanks.com/blog/react-native-tutorial-part-3-developing-a-calculator

Well basically the solution is in binding item.id along this in the assignment of the onPress event listener, like so:

renderItem={({item}) => <Text
  onPress={this.handlePress.bind(this, item.id)} 
  style={[item.classSize, item.classColor]}>
  {item.id+1} 
  {this.props.quotes[item.id]}
</Text> }

After doing so, the only thing you have to do is to define handlePress like so:

handlePress(e) {
    this.props.change(e);
}

Where e is the item.id, on the Child and change() on the Parent:

  change(number) {

  this.setState({color: [true, true, true, true, true, true, true, true, true], progress: number});

  }

The complete code works as intended.

import React, { Component } from 'react';
import { AppRegistry, FlatList, StyleSheet, Text, View, Button } from 'react-native';
import ReactNativeComponentTree from 'react-native';

export default class Parent extends Component {

  constructor(props) {
    super(props);    

    this.state = {  

            quotes: ["a","bnaskdkahhahskkdk","c","d","e","a","b","c","d"],
                size: [true, true, true, true, true, true, true, true, true],
                color: [false, false, false, false, false, false, false, false, false],
                progress: "me"

    };

    this.change = this.change.bind(this);
    this.reset = this.reset.bind(this);

  }

  change(number) {

  this.setState({color: [true, true, true, true, true, true, true, true, true], progress: number});

  }

  reset() {

    this.setState({color: [false, false, false, false, false, false, false, false, false],
                   progress: "me"
    });

  }

  render() {
    return (
      <View style={styles.container}>
<Child change={this.change} reset={this.reset} quotes={this.state.quotes} 
       size={this.state.size} color={this.state.color} 
       progress={this.state.progress} />
      </View>
    );
  }
}

class Child extends Component {

    constructor(props) {

    super(props);    

    this.handlePress = this.handlePress.bind(this);
    this.handleReset = this.handleReset.bind(this);
  }

handlePress(e) {
        this.props.change(e);
    }

    /*  handlePress(event) {

let number =  ReactNativeComponentTree.getInstanceFromNode(event.currentTarget)._currentElement.id;

this.props.change(number);

}  */

    handleReset() {
      this.props.reset();
    }

  render() {

    let ar = [];

    for (let i=0; i<this.props.quotes.length; i++) {
      let b = {key: `${i}`, id: i, 
          classSize: this.props.size[i] ? (i%2===0 ? styles.size : styles.oddsize) : "", 
          classColor: this.props.color[i] ? (i%2===0 ? styles.color : styles.oddcolor) : ""}
      ar.push(b);      

    }

    return (
    <View style={styles.container}>
      <Button onPress={this.handleReset} title="Reset" />
        <FlatList
          data={
            ar
          }

renderItem={({item}) => <Text onPress={this.handlePress.bind(this, item.id)} 
style={[item.classSize, item.classColor]}> {item.id+1} 
{this.props.quotes[item.id]} </Text> }

        /> 

    <Text style={styles.size}>{this.props.progress}</Text>

    </View>
    );
  }
}


const styles = StyleSheet.create({
  container: {
   flex: 1,
   flexDirection: "column",
   //justifyContent: "center",
   alignItems: "center",
   paddingTop: 22,
   //backgroundColor: "purple" 
  },
  size: {
    flex: 1,
    padding: 10,
    fontSize: 18,
    backgroundColor: "grey",
    margin: 1,
    height: 44,
    color: 'gold',
    borderColor: "white",
    borderWidth: "1",
    textAlign: "center"
  },
  oddsize: {
    flex: 1,
    padding: 10,
    fontSize: 18,
    backgroundColor: "white",
    margin: 1,
    height: 44,
    color: 'gold',
    borderColor: "white",
    borderWidth: "1",
    textAlign: "center"
  },
  color: {
    flex: 1,
    padding: 10,
    backgroundColor: 'grey',
    //borderRadius: "25%",
    margin: 1,
    fontSize: 18,
    height: 44,
    color: 'pink',
    borderColor: "red",
    borderWidth: "1"
  },
oddcolor: {
    flex: 1,
    padding: 10,
    backgroundColor: 'white',
    //borderRadius: "25%",
    margin: 1,
    fontSize: 18,
    height: 44,
    color: 'pink',
    borderColor: "red",
    borderWidth: "1"
  }
})

// skip this line if using Create React Native App
AppRegistry.registerComponent('AwesomeProject', () => Parent);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!