我在React上创建了一个游戏,我正在尝试将我的代码调整为React Native。困扰我的一件事是如何翻译这三行,因为在RN中没有可依赖的DOM解决方案:
handleClick(e) {
this.props.change(e.currentTarget.id);
}
这里发生的事情是一个无状态的孩子正在收获一个被点击的元素id(currentTarget's),并用它来调用父内部定义的方法。然而,这种表述e.currentTarget.id
在RN中不起作用。
有没有雄辩的方法在RN中重写这一个班轮?
注意:有两个问题模糊地类似于here和here,但答案看起来更像是补丁而不是结构优雅的解决方案。如果你知道某些事情请回答。
编辑:似乎无法绕过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);
答案 0 :(得分:4)
更好的方法(避免在每次渲染时创建事件回调)
获取当前按下的元素属性(本例中为id)
通过将其包装在父组件中,传递数据
并且只绑定所有事件一次(在构造函数中)
看到差异,这里有一个更高级的例子: 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>
);
}
}
如果使用const JSX对象(无状态功能组件),它可以工作,但它不是最佳的,每次渲染组件时都会创建事件回调,因为箭头函数实际上是渲染的快捷方式功能
const TouchableText = props => {
const textPressed = () => {
props.onPressItem(props.id);
};
return <Text onPress={textPressed} />;
};
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);