2017-08-16 59 views
1

我已經在React上創建了一個遊戲,並且我正在嘗試將我的代碼調整爲React Native。其中之一是困擾我的一件事就是如何將這些三線平移,因爲在RN沒有DOM的解決方案依賴於:React Native-- onPress從「currentTarget」中提取id

handleClick(e) { 

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

} 

這到底是怎麼發生的是一個無狀態的孩子收穫一個點擊的元素ID (currentTarget的),並用它來調用它在父級中定義的方法。然而,這種配方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); 

回答

-1

8小時後搜索我已經找到我自己的解決方案,這要歸功於凱爾銀行的夢幻般的計算器教程。

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,對家長的兒童和變化():

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

@P。 Myer Nore:這正是你在https://stackoverflow.com/questions/2236747/use-of-the-javascript-bind-method中評論的內容。 – alexandros84

+0

您不應該在'render'方法中使用JSX /中的bind或arrow函數。它們每次都會創建一個新的函數,這會導致組件被更改,導致組件每次都被重新渲染,這首先破壞了使用React的許多好處。然而,這個記錄極其糟糕。 – hippietrail

3

一種更好的方式(避免事件回調創建在每一個渲染)
來得到當前的按壓元件特性(ID在本例中)
是通過纏繞它在一個父組件,傳遞數據
和有約束力的所有事件只有一次(在構造函數)

  1. 首先聲明你的組件包裝:
    這需要一個類,而不是一個無狀態的功能組件,否則你無法避免回調建立在每一個渲染

    看出差別,這裏的行動更高級的例子:
    https://snack.expo.io/ByTEKgEsZexample 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> 
    ); 
    } 
} 

如果你使用一個const對象JSX(無狀態的功能組件),它的工作原理,但它不是最優的,事件回調將創建每次分量被呈現爲箭頭函數實際上是快捷方式的使功能

​​
  • 然後使用此包裝,而不是你組分如下:
  • 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會讓你感到困擾,那麼有兩種方法可以解決這個問題。如果您使用的是實驗屬性初始化語法,你可以使用屬性初始化到正確綁定回調

    ,這意味着我們可以只寫

    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

    啊哈!正是我在找的東西。這在SO上出現很多,但問題措詞不同,很少交叉鏈接。提供類似答案的人似乎堅持認爲包裝需要是一個類,我參與的項目使用的是常量JSX對象,但我無法找到兩者是否兼容。到現在。 – hippietrail

    +1

    謝謝你的評論!我檢查了你說的,其他人是對的。如果你想避免重新渲染事件回調,你需要包裝類成爲一個類,否則它重新創建事件回調:/你可以在這裏檢查我的測試https://snack.expo.io/ByTEKgEsZ。你可以按照如下方式將包裝器包裝到x)中來獲得一個const:const ComponentConst = props =>;我更新了我的答案,以更好地反映您的觀察結果。再次感謝! – Florent

    +0

    看到https://facebook.github.io/react-native/docs/flatlist.html也 – Florent