我面对的是React Native中一个长期存在的问题。
我正在使用带有RN版本0.59的Expo SDK35。由于代码量很大,我还没有更新到Expo SDK36 / RN 0.60,但是如果可以弥补我的问题,我可以更新。
我有一个Animated.View
子组件,它有一个FlatList
子组件,并且我无法使用FlatList引用上应该可用的静态方法(尤其是scrollToIndex()
)。请参见下一个示例代码:
class Example extends React.Component{
constructor(props){
super(props);
this.myRef = null;
}
componentDidUpdate = () => {
/*
somewhere in code outside this class, a re-render triggers
and passes new props to this class.
I do have props change detection, and some more other code,
but I have removed it in order to minimize the code example here
*/
// This call throws:
// TypeError: undefined is not a function (near '...this._scrollRef.scrollTo...')
this.myRef.scrollToIndex({
animated: true,
index: 1,
viewOffset: 0,
viewPosition: 0.5
});
// Other suggested solution from SO
// This also throws:
// TypeError: _this.myRef.getNode is not a function. (In '_this.myRef.getNode()', '_this.myRef.getNode' is undefined)
this.myRef.getNode().scrollToIndex({
animated: true,
index: 1,
viewOffset: 0,
viewPosition: 0.5
});
}
render = () => <Animated.View style={{ /* ... some animated props */ }}>
<FlatList ref={(flatListRef) => { this.myRef = flatListRef; }}
// more FlatList related props
/>
</Animated.View>
}
我尝试改用Animated.FlatList
,但仍然引发与上面的代码示例相同的错误。
我还尝试在接收到的findNodeHandle()
参数上使用react native的flatListRef
实用程序功能,但它返回null
。
我发现过去在此处的Stack Overflow上多次发布了相同的问题,大多数问题没有答案,或者对我不起作用。这些帖子也有点旧(一年左右),这就是为什么我针对同一问题再次发布。
有人设法找到此问题的解决方案/解决方法吗?
在玩代码时,我尝试使用ScrollView
组件而不是FlatList
-并且scrollTo
方法有效!
所做的更改仅在FlatList-ScrollView特定的道具上(因此,对于ScrolLView,它将是孩子,而不是data={[...]}
和renderItem={()=>{ ... }}
等),以及componentDidMount中的scrollToIndex
方法被scrollTo
取代。
带有ScrollView的类的render方法现在看起来像这样:
render = () => <Animated.View style={{ /* ... some animated props */ }}>
<ScrollView ref={(flatListRef) => { this.myRef = flatListRef; }}>
{/*
this.renderItem is almost the same as the
renderItem method used on the FlatList
*/}
{ this.state.dataArray.map(this.renderItem) }
</ScrollView>
</Animated.View>
请注意,ScrollView没有scrollToIndex()
方法,因此您必须手动跟踪子位置,并可能实现自己的scrollToIndex方法。
我并没有将这个问题作为答案,因为根本的问题仍然存在。但是,作为一种解决方法,也许您可以将其命名为“一天” ...
答案 0 :(得分:2)
this.myRef = React.createRef();
this.myRef.current.doSomething(); // note the use of 'current'
虽然我尝试的想法背后的想法是正确的,但我最初帖子中的错误似乎非常愚蠢。在我的辩护中,文档不清楚(可能是...)。反正...
React.createRef
返回一个带有几个字段的对象,所有这些字段对开发人员都无用(后面由React使用)-除了一个current
。
该属性将保持对引用附加到的基础组件的当前引用。主要的ref对象无法用于我在上面的原始问题中想要达到的目的。
相反,这就是我应该正确使用ref的方式:
this.myRef.current.scrollToIndex(...)
主要myRef
对象和current
字段都将是null
,如果该组件尚未安装,在以后的任何时候已卸载或无法连接引用由于某种原因。您可能知道(或以后发现),null.something
将引发错误。因此,为避免这种情况:
if ((this.myRef !== null) && (this.myRef.current !== null)){
this.myRef.current.scrollToIndex(...);
}
如果您尝试在ref的字段上调用未定义的值作为函数,则代码将崩溃。如果您错误地在多个组件上重用了相同的ref,或者如果附加了该组件的组件没有该方法(即View
没有scrollTo
方法),则会发生这种情况。要解决此问题,您有两种解决方案:
// I find this to be the most elegant solution
if ((this.myRef !== null) && (this.myRef.current !== null)) {
if (typeof this.myRef.current.scrollToIndex === "function") {
this.myRef.current.scrollToIndex(...);
}
}
或
if ((this.myRef !== null) && (this.myRef.current !== null)) {
if (typeof this.myRef.current.scrollToIndex === "function") {
try {
this.myRef.current.scrollToIndex(...);
} catch (error) {
console.warn("Something went wrong", error);
}
}
}
我希望这对其他在React中使用refs的人有用。干杯:)
答案 1 :(得分:0)
使用Animated.ScrollView :
<ScrollView ref={ (ref) => (this.MyRef=ref) } />
scrollToIndex
访问this.myRef.getNode().scrollToIndex
Animated.FlatList
当前无法正常工作...
使用FlatList :
<FlatList ref={ this.flatListRef } />
constructor(props) {
super(props);
this.flatListRef = React.createRef();
}
scrollToIndex
访问this.flatListRef.current.scrollToIndex
还要确保将代码包装在if语句中,例如:
if (this.myRef.getNode()) { this.flatListRef.getNode().scrollToIndex(); }
答案 2 :(得分:0)
o不知道这是否对您有帮助...它滚动到列表中的一个特定项目:
/*Example to Scroll to a specific position in scrollview*/
import React, { Component } from 'react';
//import react in our project
import {
View,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
Image,
TextInput,
} from 'react-native';
//import all the components we needed
export default class App extends Component {
constructor() {
super();
//Array of Item to add in Scrollview
this.items = [
'zero',
'one',
'two',
'three',
'four',
'five',
'six',
'seven',
'eight',
'nine',
'ten ',
'eleven',
'twelve',
'thirteen',
'fourteen',
'fifteen',
'sixteen',
'seventeen',
'eighteen',
'nineteen',
'twenty ',
'twenty-one',
'twenty-two',
'twenty-three',
'twenty-four',
'twenty-five',
'twenty-six',
'twenty-seven',
'twenty-eight',
'twenty-nine',
'thirty',
'thirty-one',
'thirty-two',
'thirty-three',
'thirty-four',
'thirty-five',
'thirty-six',
'thirty-seven',
'thirty-eight',
'thirty-nine',
'forty',
];
//Blank array to store the location of each item
this.arr = [];
this.state = { dynamicIndex: 0 };
}
downButtonHandler = () => {
if (this.arr.length >= this.state.dynamicIndex) {
// To Scroll to the index 5 element
this.scrollview_ref.scrollTo({
x: 0,
y: this.arr[this.state.dynamicIndex],
animated: true,
});
} else {
alert('Out of Max Index');
}
};
render() {
return (
<View style={styles.container}>
<View
style={{
flexDirection: 'row',
backgroundColor: '#1e73be',
padding: 5,
}}>
<TextInput
value={String(this.state.dynamicIndex)}
numericvalue
keyboardType={'numeric'}
onChangeText={dynamicIndex => this.setState({ dynamicIndex })}
placeholder={'Enter the index to scroll'}
style={{ flex: 1, backgroundColor: 'white', padding: 10 }}
/>
<TouchableOpacity
activeOpacity={0.5}
onPress={this.downButtonHandler}
style={{ padding: 15, backgroundColor: '#f4801e' }}>
<Text style={{ color: '#fff' }}>Go to Index</Text>
</TouchableOpacity>
</View>
<ScrollView
ref={ref => {
this.scrollview_ref = ref;
}}>
{/*Loop of JS which is like foreach loop*/}
{this.items.map((item, key) => (
//key is the index of the array
//item is the single item of the array
<View
key={key}
style={styles.item}
onLayout={event => {
const layout = event.nativeEvent.layout;
this.arr[key] = layout.y;
console.log('height:', layout.height);
console.log('width:', layout.width);
console.log('x:', layout.x);
console.log('y:', layout.y);
}}>
<Text style={styles.text}>
{key}. {item}
</Text>
<View style={styles.separator} />
</View>
))}
</ScrollView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 30,
},
separator: {
height: 1,
backgroundColor: '#707080',
width: '100%',
},
text: {
fontSize: 16,
color: '#606070',
padding: 10,
},
});
如果我完全错了,告诉我...
答案 3 :(得分:0)
因为ScrollView没有scrollToOffset
功能,而只有scrollTo
功能。
因此,让功能scrollTo
与ScrollView一起使用,或者scrollToOffset
与FlatList一起使用,它可以正常工作。