问题描述
我已经在React上创建了一个游戏,我正在尝试使我的代码适应React Native.困扰我的一件事是如何翻译这三行代码,因为在RN中,没有DOM解决方案可依靠:
I have created a game on React and I am trying to adapt my code to React Native. One of the things that is troubling me is how to translate these three lines, since in RN there are no DOM solutions to rely on:
handleClick(e) {
this.props.change(e.currentTarget.id);
}
这里发生的是一个无状态的孩子正在收获一个被单击元素的id(currentTarget的id),并使用它来调用在父对象中定义的方法.但是,这种公式e.currentTarget.id
在RN中不起作用.
What is happening here is that a stateless child is harvesting a clicked elements id (the currentTarget's) and is using it to call with it a method defined inside the parent. This kind of formulation e.currentTarget.id
however does not work in RN.
是否有一种雄辩的方法可以在RN中重写这一衬里?
Is there an eloquent way to re-write this one liner in RN?
注意:有两个问题与此相似,和,但是答案看起来更像是补丁,而不是结构优美的解决方案.如果您知道某些事情,请发布答案.
Note: there are two questions vaguely resembling this one, here and here, however the answers look like more like patches than a structural elegant solution. If you are aware of something pls post an answer.
编辑:似乎无法绕过ReactNativeComponentTree.
Edit: It seems that one cannot go around ReactNativeComponentTree.
到目前为止,我有很多东西,但这还行不通:
I have that much so far but this does not work yet:
handlePress(event) {
let number = ReactNativeComponentTree.getInstanceFromNode(event.currentTarget)._currentElement.id;
this.props.change(number);
}
第二好吧,也许我应该添加一个简单示例说明我要实现的目标.当我单击单位列表的任何元素时,其ID应显示在儿童"的底部.单击重置将恢复默认状态.
Second Ok maybe I should add a simplistic example of what I am trying to achieve. When I click on any of the flatlist's elements its id should be displayed on the Child's bottom . Clicking on reset will restore default state.
下面的简单示例代码:
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);
推荐答案
更好的方法(避免在每个渲染器中创建事件回调)
获取当前按下的元素属性(在此示例中为id)
是通过将其包装在父组件中,并传递数据
并只绑定一次所有事件(在构造函数中)
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)
- 首先声明您的组件包装器:
它必须是一个类,而不是无状态的功能组件,否则您将无法避免在每个渲染器上都创建回调
- 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
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.textPressed}>
{this.props.children}
</Text>
);
}
}
如果使用const JSX对象(无状态功能组件),它可以工作,但不是最佳选择,因为箭头函数实际上是渲染函数的快捷方式,所以每次渲染该组件时都会创建事件回调
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} />;
};
- 然后使用此包装而不是您的组件,如下所示:
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 所述,还有另一个避免绑定到构造函数中的可能语法(不过是实验性的:即将来可能会或可能不支持!):
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!):
这意味着我们只能写
handlePress = (id) => {
//`this` is already bound!
}
代替
constructor(props) {
super(props);
//manually bind `this` in the constructor
this.handlePress = this.handlePress.bind(this);
}
handlePress(id) {
}
一些参考文献:
事件处理程序和无状态功能组件
反应绑定模式:处理此问题的5种方法
Some references:
Event handlers and Functional Stateless Components
React Binding Patterns: 5 Approaches for Handling this
这篇关于反应本机-onPress从"currentTarget"中提取ID.的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!