任何状态更改时的 FlatList ScrollView 错误 - 不变违规:不支持即时更改 onViewableItemsChanged
FlatList ScrollView Error on any State Change - Invariant Violation: Changing onViewableItemsChanged on the fly is not supported
onViewableItemsChanged 在应用程序中发生状态更改时似乎不起作用。这是正确的吗?
如果是这样的话,好像用处不大....
否则,用户将被迫向我们onScroll
确定位置或类似的东西...
重现步骤
注意:将 onViewableItemsChanged
函数放在渲染方法之外的 const
中也无济于事...
<FlatList
data={this.state.cardData}
horizontal={true}
pagingEnabled={true}
showsHorizontalScrollIndicator={false}
onViewableItemsChanged={(info) =>console.log(info)}
viewabilityConfig={{viewAreaCoveragePercentThreshold: 50}}
renderItem={({item}) =>
<View style={{width: width, borderColor: 'white', borderWidth: 20,}}>
<Text>Dogs and Cats</Text>
</View>
}
/>
实际行为
错误
将您的 viewabilityConfig 属性移除为渲染函数和 onViewableItemsChanged 函数之外的常量值
您必须将函数传递给绑定在组件构造函数中的 onViewableItemsChanged
,并且必须将 viewabilityConfig
设置为 Flatlist
.[=14 之外的常量=]
示例:
class YourComponent extends Component {
constructor() {
super()
this.onViewableItemsChanged.bind(this)
}
onViewableItemsChanged({viewableItems, changed}) {
console.log('viewableItems', viewableItems)
console.log('changed', changed)
}
viewabilityConfig = {viewAreaCoveragePercentThreshold: 50}
render() {
return(
<FlatList
data={this.state.cardData}
horizontal={true}
pagingEnabled={true}
showsHorizontalScrollIndicator={false}
onViewableItemsChanged={this.onViewableItemsChanged}
viewabilityConfig={this.viewabilityConfig}
renderItem={({item}) =>
<View style={{width: width, borderColor: 'white', borderWidth: 20,}}>
<Text>Dogs and Cats</Text>
</View>}
/>
)
}
}
将 viewabilityConfig 对象移至构造函数。
constructor() {
this.viewabilityConfig = {
viewAreaCoveragePercentThreshold: 50
};
}
render() {
return(
<FlatList
data={this.state.cardData}
horizontal={true}
pagingEnabled={true}
showsHorizontalScrollIndicator={false}
onViewableItemsChanged={(info) =>console.log(info)}
viewabilityConfig={this.viewabilityConfig}
renderItem={({item}) =>
<View style={{width: width, borderColor: 'white', borderWidth: 20,}}>
<Text>Dogs and Cats</Text>
</View>
}
/>
)
}
有人建议使用 Flatlist 的 extraData
属性 让 Flatlist 注意到,有些东西发生了变化。
但这对我不起作用,以下是对我有用的方法:
使用 key={this.state.orientation}
而 orientation
例如 "portrait" 或 "landscape"...它可以是你想要的任何东西,但它必须改变,如果方向改变.
如果 Flatlist 注意到 key-属性 发生了变化,它会重新渲染。
适用于 react-native 0.56
基于@woodpav 评论。使用功能组件和 Hooks。
将 viewabilityConfig
和 onViewableItemsChanged
分配给 refs 并使用它们。如下所示:
const onViewRef = React.useRef((viewableItems)=> {
console.log(viewableItems)
// Use viewable items in state or as intended
})
const viewConfigRef = React.useRef({ viewAreaCoveragePercentThreshold: 50 })
<FlatList
horizontal={true}
onViewableItemsChanged={onViewRef.current}
data={Object.keys(cards)}
keyExtractor={(_, index) => index.toString()}
viewabilityConfig={viewConfigRef.current}
renderItem={({ item, index }) => { ... }}
/>
这对我有用,有什么方法可以将附加参数传递给 onViewRef 吗?就像下面的代码一样,我如何将类型参数传递给 onViewRef。
代码:
function getScrollItems(items, isPendingList, type) {
return (
<FlatList
data={items}
style={{width: wp("100%"), paddingLeft: wp("4%"), paddingRight: wp("10%")}}
horizontal={true}
keyExtractor={(item, index) => index.toString()}
showsHorizontalScrollIndicator={false}
renderItem={({item, index}) => renderScrollItem(item, index, isPendingList, type)}
viewabilityConfig={viewConfigRef.current}
onViewableItemsChanged={onViewRef.current}
/>
)
}
错误“不支持即时更改 onViewableItemsChanged
”,因为当您更新状态时,您正在创建一个新的 onViewableItemsChanged
函数参考,因此您正在即时更改它。
虽然 可以解决 useRef
的问题,但在这种情况下它不是正确的钩子。您应该使用 useCallback
到 return 记忆回调和 useState
来获取当前状态,而无需创建对该函数的新引用。
下面是一个保存所有已查看项目索引的示例:
const MyComp = () => {
const [cardData] = useState(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']);
const [viewedItems, setViewedItems] = useState([]);
const handleVieweableItemsChanged = useCallback(({ changed }) => {
setViewedItems(oldViewedItems => {
// We can have access to the current state without adding it
// to the useCallback dependencies
let newViewedItems = null;
changed.forEach(({ index, isViewable }) => {
if (index != null && isViewable && !oldViewedItems.includes(index)) {
if (newViewedItems == null) {
newViewedItems = [...oldViewedItems];
}
newViewedItems.push(index);
}
});
// If the items didn't change, we return the old items so
// an unnecessary re-render is avoided.
return newViewedItems == null ? oldViewedItems : newViewedItems;
});
// Since it has no dependencies, this function is created only once
}, []);
function renderItem({ index, item }) {
const viewed = '' + viewedItems.includes(index);
return (
<View>
<Text>Data: {item}, Viewed: {viewed}</Text>
</View>
);
}
return (
<FlatList
data={cardData}
onViewableItemsChanged={handleVieweableItemsChanged}
viewabilityConfig={this.viewabilityConfig}
renderItem={renderItem}
/>
);
}
您可以看到它在 Snack 上运行。
const handleItemChange = useCallback( ({viewableItems}) => {
console.log('here are the chaneges', viewableItems);
if(viewableItems.length>=1)
viewableItems[0].isViewable?
setChange(viewableItems[0].index):null;
},[])
试试这个对我有用
在平面列表外设置 onViewableItemsChanged 和 viewabilityConfig 解决了我的问题。
const onViewableItemsChanged = useCallback(({ viewableItems }) => {
if (viewableItems.length >= 1) {
if (viewableItems[0].isViewable) {
setItem(items[viewableItems[0].index]);
setActiveIndex(viewableItems[0].index);
}
}
}, []);
const viewabilityConfig = {
viewAreaCoveragePercentThreshold: 50,
};
我正在使用功能组件,我的平面列表如下所示
<Animated.FlatList
data={items}
keyExtractor={item => item.key}
horizontal
initialScrollIndex={activeIndex}
pagingEnabled
onViewableItemsChanged={onViewableItemsChanged}
viewabilityConfig={viewabilityConfig}
ref={flatlistRef}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { x: scrollX } } }],
{ useNativeDriver: false },
)}
contentContainerStyle={{
paddingBottom: 10,
}}
showsHorizontalScrollIndicator={false}
renderItem={({ item }) => {
return (
<View style={{ width, alignItems: 'center' }}>
<SomeComponent item={item} />
</View>
);
}}
/>
onViewableItemsChanged 在应用程序中发生状态更改时似乎不起作用。这是正确的吗?
如果是这样的话,好像用处不大....
否则,用户将被迫向我们onScroll
确定位置或类似的东西...
重现步骤
注意:将 onViewableItemsChanged
函数放在渲染方法之外的 const
中也无济于事...
<FlatList
data={this.state.cardData}
horizontal={true}
pagingEnabled={true}
showsHorizontalScrollIndicator={false}
onViewableItemsChanged={(info) =>console.log(info)}
viewabilityConfig={{viewAreaCoveragePercentThreshold: 50}}
renderItem={({item}) =>
<View style={{width: width, borderColor: 'white', borderWidth: 20,}}>
<Text>Dogs and Cats</Text>
</View>
}
/>
实际行为
错误
将您的 viewabilityConfig 属性移除为渲染函数和 onViewableItemsChanged 函数之外的常量值
您必须将函数传递给绑定在组件构造函数中的 onViewableItemsChanged
,并且必须将 viewabilityConfig
设置为 Flatlist
.[=14 之外的常量=]
示例:
class YourComponent extends Component {
constructor() {
super()
this.onViewableItemsChanged.bind(this)
}
onViewableItemsChanged({viewableItems, changed}) {
console.log('viewableItems', viewableItems)
console.log('changed', changed)
}
viewabilityConfig = {viewAreaCoveragePercentThreshold: 50}
render() {
return(
<FlatList
data={this.state.cardData}
horizontal={true}
pagingEnabled={true}
showsHorizontalScrollIndicator={false}
onViewableItemsChanged={this.onViewableItemsChanged}
viewabilityConfig={this.viewabilityConfig}
renderItem={({item}) =>
<View style={{width: width, borderColor: 'white', borderWidth: 20,}}>
<Text>Dogs and Cats</Text>
</View>}
/>
)
}
}
将 viewabilityConfig 对象移至构造函数。
constructor() {
this.viewabilityConfig = {
viewAreaCoveragePercentThreshold: 50
};
}
render() {
return(
<FlatList
data={this.state.cardData}
horizontal={true}
pagingEnabled={true}
showsHorizontalScrollIndicator={false}
onViewableItemsChanged={(info) =>console.log(info)}
viewabilityConfig={this.viewabilityConfig}
renderItem={({item}) =>
<View style={{width: width, borderColor: 'white', borderWidth: 20,}}>
<Text>Dogs and Cats</Text>
</View>
}
/>
)
}
有人建议使用 Flatlist 的 extraData
属性 让 Flatlist 注意到,有些东西发生了变化。
但这对我不起作用,以下是对我有用的方法:
使用 key={this.state.orientation}
而 orientation
例如 "portrait" 或 "landscape"...它可以是你想要的任何东西,但它必须改变,如果方向改变.
如果 Flatlist 注意到 key-属性 发生了变化,它会重新渲染。
适用于 react-native 0.56
基于@woodpav 评论。使用功能组件和 Hooks。
将 viewabilityConfig
和 onViewableItemsChanged
分配给 refs 并使用它们。如下所示:
const onViewRef = React.useRef((viewableItems)=> {
console.log(viewableItems)
// Use viewable items in state or as intended
})
const viewConfigRef = React.useRef({ viewAreaCoveragePercentThreshold: 50 })
<FlatList
horizontal={true}
onViewableItemsChanged={onViewRef.current}
data={Object.keys(cards)}
keyExtractor={(_, index) => index.toString()}
viewabilityConfig={viewConfigRef.current}
renderItem={({ item, index }) => { ... }}
/>
这对我有用,有什么方法可以将附加参数传递给 onViewRef 吗?就像下面的代码一样,我如何将类型参数传递给 onViewRef。 代码:
function getScrollItems(items, isPendingList, type) {
return (
<FlatList
data={items}
style={{width: wp("100%"), paddingLeft: wp("4%"), paddingRight: wp("10%")}}
horizontal={true}
keyExtractor={(item, index) => index.toString()}
showsHorizontalScrollIndicator={false}
renderItem={({item, index}) => renderScrollItem(item, index, isPendingList, type)}
viewabilityConfig={viewConfigRef.current}
onViewableItemsChanged={onViewRef.current}
/>
)
}
错误“不支持即时更改 onViewableItemsChanged
”,因为当您更新状态时,您正在创建一个新的 onViewableItemsChanged
函数参考,因此您正在即时更改它。
虽然 useRef
的问题,但在这种情况下它不是正确的钩子。您应该使用 useCallback
到 return 记忆回调和 useState
来获取当前状态,而无需创建对该函数的新引用。
下面是一个保存所有已查看项目索引的示例:
const MyComp = () => {
const [cardData] = useState(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']);
const [viewedItems, setViewedItems] = useState([]);
const handleVieweableItemsChanged = useCallback(({ changed }) => {
setViewedItems(oldViewedItems => {
// We can have access to the current state without adding it
// to the useCallback dependencies
let newViewedItems = null;
changed.forEach(({ index, isViewable }) => {
if (index != null && isViewable && !oldViewedItems.includes(index)) {
if (newViewedItems == null) {
newViewedItems = [...oldViewedItems];
}
newViewedItems.push(index);
}
});
// If the items didn't change, we return the old items so
// an unnecessary re-render is avoided.
return newViewedItems == null ? oldViewedItems : newViewedItems;
});
// Since it has no dependencies, this function is created only once
}, []);
function renderItem({ index, item }) {
const viewed = '' + viewedItems.includes(index);
return (
<View>
<Text>Data: {item}, Viewed: {viewed}</Text>
</View>
);
}
return (
<FlatList
data={cardData}
onViewableItemsChanged={handleVieweableItemsChanged}
viewabilityConfig={this.viewabilityConfig}
renderItem={renderItem}
/>
);
}
您可以看到它在 Snack 上运行。
const handleItemChange = useCallback( ({viewableItems}) => {
console.log('here are the chaneges', viewableItems);
if(viewableItems.length>=1)
viewableItems[0].isViewable?
setChange(viewableItems[0].index):null;
},[])
试试这个对我有用
在平面列表外设置 onViewableItemsChanged 和 viewabilityConfig 解决了我的问题。
const onViewableItemsChanged = useCallback(({ viewableItems }) => {
if (viewableItems.length >= 1) {
if (viewableItems[0].isViewable) {
setItem(items[viewableItems[0].index]);
setActiveIndex(viewableItems[0].index);
}
}
}, []);
const viewabilityConfig = {
viewAreaCoveragePercentThreshold: 50,
};
我正在使用功能组件,我的平面列表如下所示
<Animated.FlatList
data={items}
keyExtractor={item => item.key}
horizontal
initialScrollIndex={activeIndex}
pagingEnabled
onViewableItemsChanged={onViewableItemsChanged}
viewabilityConfig={viewabilityConfig}
ref={flatlistRef}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { x: scrollX } } }],
{ useNativeDriver: false },
)}
contentContainerStyle={{
paddingBottom: 10,
}}
showsHorizontalScrollIndicator={false}
renderItem={({ item }) => {
return (
<View style={{ width, alignItems: 'center' }}>
<SomeComponent item={item} />
</View>
);
}}
/>