Andy Root
Andy Root

Reputation: 83

How to read props from a React Native touchable event currentTarget?

I have the following React Native code that runs the press() method when a user taps an image. I want to get the itemIndex prop from the event object. I set a break point in the press method and added some expressions to the Watch. From the Watch I determined that the target (event origination) from the event is the Image which is correct. The itemIndex prop is also available. The element being processed is the currentTarget, the Watch sees it's a "RCTView" and I was expecting a TouchableOpacity, so maybe underneath TouchableOpacity is a View? The currentTarget itemIndex prop is undefined, why? How can I get the props from the currentTarget?

I want to do it this way to avoid creating addition methods for each rendered item.

FYI, ref={(c) => this._input = c} will not work because it's being run in a loop. onPress={(e) => this.press(e, i)} creates a new function which I'm trying to avoid.

Watch

  1. target._currentElement.props.itemIndex: 2
  2. target._currentElement.type.displayName: "RCTImageView"
  3. currentTarget._currentElement.props.itemIndex: undefined
  4. currentTarget._currentElement.type.displayName: "RCTView"

    press: function(event){
        var currentTarget = ReactNativeComponentTree.getInstanceFromNode(event.currentTarget);
        var target = ReactNativeComponentTree.getInstanceFromNode(event.target);
        var currentTargetIndex = currentTarget._currentElement.props.itemIndex;
        var targetIndex = target._currentElement.props.itemIndex;
        var url = this.state.data.items[currentTargetIndex].url;
        Linking.openURL(url).catch(err => console.error('An error occurred', err));
    },
    
    render: function() {
    return (
        <ScrollView horizontal={true} showsHorizontalScrollIndicator={false} style={styles.galleryView}>
            {
                this.state.data.items.map((data, i) =>
                    <TouchableOpacity itemIndex={i} key={i} activeOpacity={0.5} onPress={this.press} >
                        <Image itemIndex={i} key={i} source={{uri:data.previewImageUri}} style={styles.galleryImage} />
                    </TouchableOpacity>
                )
            }
        </ScrollView>
    );
    }
    

Upvotes: 8

Views: 9095

Answers (3)

PrashanD
PrashanD

Reputation: 2764

You could make your event handler a curried function that accepts extra parameters.

 //Curried function for onPress event handler
  handleOnPress = (someId, someProp) => event => {
    //USE someProp ABOVE TO ACCESS PASSED PROP, WHICH WOULD BE undefined IN THIS CASE
    //Use event parameter above to access event object if you need
    console.log(someProp)

    this.setState({
      touchedId: someId
    })
  }

Checkout the working snack below

https://snack.expo.io/@prashand/accessing-props-from-react-native-touch-event

Upvotes: 2

Matt Aft
Matt Aft

Reputation: 8936

I actually came across this same issue recently, I found two different ways you could approach this. The easier way of doing it is altering your onPress to pass an index to your press function, this is the 2nd way of doing it:

press: function(event, index){
  var url = this.state.data.items[index].url;
  Linking.openURL(url).catch(err => console.error('An error occurred', err));
},

render: function() {
  return (
    <ScrollView
      horizontal={true}
      showsHorizontalScrollIndicator={false}
      style={styles.galleryView}
    >
    {
      this.state.data.items.map((data, i) =>
        <Images data={data} key={i} index={i} press={this.press} />
      )
    }
    </ScrollView>
    );
}

const Images = (props) => {
  const imageClicked = (e) => {
    props.press(e, props.index);
  }
  return (
    <TouchableOpacity activeOpacity={0.5} onPress={imageClicked} >
      <Image source={{uri:props.data.previewImageUri}} style={styles.galleryImage} />
    </TouchableOpacity>
  )
}

Upvotes: 3

Fareed Stevenson
Fareed Stevenson

Reputation: 16

Binding the needed information to a callback and assigning one to each child avoids recreating the callback on every render of children.

class Hello extends React.Component{
  state = { names: this.props.names.map((name, i) => {
      return Object.assign({
        onClick: this._onClick.bind(this, i, this.props),
      }, name)
    }),
  };

  _onClick(ind, _props, e) {
    alert('props:' + JSON.stringify(_props));
  }

  render() {
    const { names } = this.state;
    return (
      <div>
        { names.map((name, i) => (
            <div key={i}>Name: <input value={ name.first } onClick={ name.onClick } /></div>
          ))}
      </div>
  )}
}

ReactDOM.render(
  <Hello names={[{first:'aaa'},{first:'bbb'},{first:'ccc'}]}/>,
  document.getElementById('container')
);

JS Fiddle

Upvotes: 0

Related Questions