React Native-- onPress从“currentTarget”中提取id

时间:2017-08-16 06:13:07

标签: react-native onpress

我在React上创建了一个游戏,我正在尝试将我的代码调整为React Native。困扰我的一件事是如何翻译这三行,因为在RN中没有可依赖的DOM解决方案:

handleClick(e) {

this.props.change(e.currentTarget.id);

}  

这里发生的事情是一个无状态的孩子正在收获一个被点击的元素id(currentTarget's),并用它来调用父内部定义的方法。然而,这种表述e.currentTarget.id在RN中不起作用。

有没有雄辩的方法在RN中重写这一个班轮?

注意:有两个问题模糊地类似于herehere,但答案看起来更像是补丁而不是结构优雅的解决方案。如果你知道某些事情请回答。

编辑:似乎无法绕过ReactNativeComponentTree。

到目前为止我已经有了这么多,但这还不行:

handlePress(event) {

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

this.props.change(number);

}  

第二次编辑:好吧也许我应该添加一个简单的例子,说明我想要实现的目标。当我点击任何flatlist的元素时,它的id应显示在Child的底部。单击重置将恢复默认状态。

以下简单示例代码:

    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) {
          let number = e.currentTarget.id;
            this.props.change(number);
        }*/

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

2 个答案:

答案 0 :(得分:4)

更好的方法(避免在每次渲染时创建事件回调)
获取当前按下的元素属性(本例中为id)
通过将其包装在父组件中,传递数据
并且只绑定所有事件一次(在构造函数中)

  1. 首先声明组件包装器:
    它必须是一个类,而不是一个无状态的功能组件,否则你无法避免在每次渲染时创建回调
      

    看到差异,这里有一个更高级的例子:   https://snack.expo.io/ByTEKgEsZexample source code

  2. 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>
        );
      }
    }
    

    如果使用const JSX对象(无状态功能组件),它可以工作,但它不是最佳的,每次渲染组件时都会创建事件回调,因为箭头函数实际上是渲染的快捷方式功能

    const TouchableText = props => {
      const textPressed = () => {
        props.onPressItem(props.id);
      };
      return <Text onPress={textPressed} />;
    };
    
    1. 然后使用此包装器代替您的组件,如下所示:
    2. 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>
          );
        }
      }
      

      正如React Native Handling Events Doc所述,还有另一种可能的语法来避免在构造函数中进行绑定(尽管是实验性的:即将来可能会支持或不支持!):

        

      如果调用bind会让你烦恼,有两种方法可以解决这个问题。如果您使用 experimental 属性初始值设定项语法,则可以使用属性初始值设定项来正确绑定回调

      这意味着我们只能写

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

      而不是

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

      +

      handlePress(id) {
      
      }
      

      一些参考文献:
      Event handlers and Functional Stateless Components
      React Binding Patterns: 5 Approaches for Handling this

答案 1 :(得分:-1)

经过8个小时的搜索,我自己找到了解决方案,这要归功于Kyle Banks精彩的计算器教程。

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

基本上解决方案是在onPress事件监听器的赋值中绑定item.id,如下所示:

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

在这样做之后,你唯一需要做的就是像这样定义handlePress:

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

其中e是item.id,在Child上并且更改()在Parent:

  change(number) {

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

  }

完整的代码按预期工作。

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