React Native——onPress 从“currentTarget”中提取 id
Posted
技术标签:
【中文标题】React Native——onPress 从“currentTarget”中提取 id【英文标题】:React Native-- onPress extract id from "currentTarget" 【发布时间】:2018-01-24 03:58:39 【问题描述】:我已经在 React 上创建了一个游戏,我正在尝试使我的代码适应 React Native。困扰我的一件事是如何翻译这三行,因为在 RN 中没有可依赖的 DOM 解决方案:
handleClick(e)
this.props.change(e.currentTarget.id);
这里发生的情况是,一个无状态的子节点正在获取一个被点击的元素 id(currentTarget 的),并使用它来调用父节点内部定义的方法。但是这种公式e.currentTarget.id
在 RN 中不起作用。
有没有一种雄辩的方法可以在 RN 中重写这一行?
注意:有两个问题与这个问题有点相似,here 和 here,但是答案看起来更像是补丁,而不是结构优雅的解决方案。如果您知道某些事情,请发布答案。
编辑:似乎无法绕过 ReactNativeComponentTree。
到目前为止我有这么多,但这还不行:
handlePress(event)
let number = ReactNativeComponentTree.getInstanceFromNode(event.currentTarget)._currentElement.id;
this.props.change(number);
第二次编辑:好吧,也许我应该添加一个简单的例子来说明我想要实现的目标。当我点击任何平面列表的元素时,它的 id 应该显示在 Child's bottom 上。点击重置将恢复默认状态。
下面的简单示例代码:
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】:更好的方法(避免在每次渲染时创建事件回调) 获取当前按下的元素属性(本例中为 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.textPressed>
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 ComponentsReact Binding Patterns: 5 Approaches for Handling this
【讨论】:
啊哈!正是我想要的。这在 SO 上出现了很多,但问题的措辞不同,而且很少交叉链接。提供类似答案的人似乎坚持认为包装器必须是我参与的项目使用 const JSX 对象的类,而我无法找到两者是否兼容。到现在为止。 感谢您的评论!我检查了你所说的,实际上其他人是对的。如果你想避免重新渲染事件回调,你需要包装器成为一个类,否则它会在每次重新渲染时重新创建事件回调:/你可以在这里查看我的测试snack.expo.io/ByTEKgEsZ。您可以通过 x)) 包装包装器,如下所示以获得 const: const ComponentConst = props =>经过 8 小时的搜索,我自己找到了解决方案,这要感谢 Kyle Banks 出色的计算器教程。
https://kylewbanks.com/blog/react-native-tutorial-part-3-developing-a-calculator
基本上解决方案是在 onPress 事件侦听器的分配中沿 this 绑定 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():
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);
【讨论】:
@P. Myer Nore:这正是您在***.com/questions/2236747/… 中评论的内容。 您不应该在render
方法中使用 JSX / 中的 bind
或箭头函数。他们每次都创建一个新函数,这会导致组件被更改,从而导致每次都重新渲染组件,这首先破坏了使用 React 的大部分好处。然而,这方面的记录极差。以上是关于React Native——onPress 从“currentTarget”中提取 id的主要内容,如果未能解决你的问题,请参考以下文章
react-native-swipeout onPress 方法禁用包含组件的 onPress 方法
React Native Hamburger onPress 问题