React-另一个支持 VirtualizedList 的本机容器

升级到原生0.61之后,我收到了很多这样的警告:

VirtualizedLists should never be nested inside plain ScrollViews with the same orientation - use another VirtualizedList-backed container instead.

我应该使用的另一个 VirtualizedList-backed container是什么,为什么现在建议不要这样使用?

182522 次浏览

看看文档中的示例,我已经将容器从:

<ScrollView>
<FlatList ... />
</ScrollView>

致:

<SafeAreaView style=\{\{flex: 1}}>
<FlatList ... />
</SafeAreaView>

所有的警告都消失了。

如果有人仍然在寻找对@Ponleu 和@David Schilling 在这里描述的问题的建议(关于超出平面列表的内容) ,那么这就是我采取的方法:

<SafeAreaView style=\{\{flex: 1}}>
<FlatList
data={data}
ListHeaderComponent={ContentThatGoesAboveTheFlatList}
ListFooterComponent={ContentThatGoesBelowTheFlatList} />
</SafeAreaView>

你可以在这里了解更多: https://facebook.github.io/react-native/docs/flatlist#listheadercomponent

希望它能帮到别人。 :)

以防这对某人有帮助,这就是我如何修复我的案例中的错误。

我把一个 FlatList嵌套在一个 ScrollView:

render() {
return (
<ScrollView>
<Text>{'My Title'}</Text>
<FlatList
data={this.state.myData}
renderItem={({ item }) => {
return <p>{item.name}</p>;
}}
/>
{this.state.loading && <Text>{'Loading...'}</Text>}
</ScrollView>
);
}

通过使用 FlatList渲染我需要的所有东西,我摆脱了 ScrollView,摆脱了警告:

render() {
const getHeader = () => {
return <Text>{'My Title'}</Text>;
};


const getFooter = () => {
if (this.state.loading) {
return null;
}
return <Text>{'Loading...'}</Text>;
};


return (
<FlatList
data={this.state.myData}
renderItem={({ item }) => {
return <p>{item.name}</p>;
}}
ListHeaderComponent={getHeader}
ListFooterComponent={getFooter}
/>
);
}

在我看来,我可以使用地图而不是平面列表。但在我的情况下,我不想显示大名单。不使用 FlatList 可能会导致性能问题。所以我用这个来抑制警告 https://github.com/GeekyAnts/NativeBase/issues/2947#issuecomment-549210509

出现这个警告是因为 ScrollViewFlatList共享相同的逻辑,如果 FlatListScrollView内部运行,它将被复制

顺便说一下,SafeAreaView对我不起作用,唯一的解决办法是

<ScrollView>
{data.map((item, index) => {
...your code
}}
</ScrollView>

错误消失

我尝试了一些方法来解决这个问题,包括 ListHeaderComponentListFooterComponent,但它们都不适合我。

我想实现的布局是这样的,我想滚动一次。

<ScrollView>
<View>I'm the first view</View>
<View>I'm the second view</View>
<MyFlatList />
</ScrollView>

首先,我想说感谢 这个的问题和意见,这给了我一些想法。

我考虑的是 ListHeaderComponent在平面表上的位置,但是因为我的 Flatlist的方向是列,所以我想放在 Flatlist左边的标题是: (

然后我不得不试了 VirtualizedList-backed的东西。我只是尝试在 VirtualizedList中打包所有的组件,其中 renderItems给出了索引,在那里我可以有条件地将组件传递给 renderItems

我本可以用 Flatlist来解决这个问题,但我还没有试过。
终于变成这样了。

<View>
<Virtualizedlist
data={[]}
initialNumToRender={1}
renderItem={(props)=>{
props.index===0 ? (1st view here) : props.index===1 ? (2nd view here) : (my flatlist)
}}
keyExtractor={item => item.key}
getItemCount={2}
getItem={ (data, index) => {
return {
id: Math.random().toString(12).substring(0),
}
}}
/>
</View>


(inside which lazyly renders↓)
<View>I'm the first view</View>
<View>I'm the second view</View>
<MyFlatList />

当然还能滚动整个屏幕。

如上所述,将 horizontal属性设置为 true似乎解决了嵌入在 ScrollView中的 FlatList的问题。

实际上,据我所知,使用嵌套的 VirtualizedList 总是会导致性能问题,只是对该问题的警告是新的。我试了网上找到的所有方法,都没用。我现在使用 ScrollView,或者当你只有一个带 maxHeight 的普通视图时,如果内容高度大于你视图的 maxHeight,你就可以滚动。 所以:

<ScrollView>
{items.map((item, index) =>
<YourComponent key={index} item={item} />
)}
</ScrollView>

或者只是:

<View style=\{\{maxHeight: MAX_HEIGHT}}>
{items.map((item, index) =>
<YourComponent key={index} item={item} />
)}
</View>

由于在 ScrollView 中使用 FlatList,此错误消失。

<SafeAreaView style={styles.container}>
<View style={styles.container}>
<View>
<Header />
</View>


{(list.length == 0) &&
<View style=\{\{flex:1, margin: 15}}>
<Text style=\{\{textAlign: 'center'}}>No peripherals</Text>
</View>
}
<FlatList
data={list}
renderItem={({ item }) => this.renderItem(item) }
keyExtractor={item => item.id}
/>
</View>
</SafeAreaView>

百科

// dummy data array
const data = [
{id: 1, name: 'Tom'},
{id: 2, name: 'Jerry'},
]

解决方案一

您可以像这样为它制作一个自定义组件

const VirtualizedList = ({children}) => {
return (
<FlatList
data={[]}
keyExtractor={() => "key"}
renderItem={null}
ListHeaderComponent={
<>{children}</>
}
/>
)
}

然后使用这个 VirtualizedList 作为父组件:

...
return (
<VirtualizedList>
<FlatList
data={data}
keyExtractor={(item, index) => item.id + index.toString()}
renderItem={_renderItem}
/>
<AnyComponent/>
</VirtualizedList>
)

解决方案二

如果您在 ScrollView 中使用 FlatList,它会给出恼人的警告,因此您可以使用 array 的 map 属性,如下所示-

注意: 不推荐以这种方式显示列表。如果你有少量的数据,那么你可以使用它,这是完全可以的,但是如果你想显示一个列表,它从 api 获取数据,并且有大量的数据,那么你可以使用其他的解决方案。如果你使用带有大量数据的 map,那么它会影响你的应用程序性能

<ScrollView>
{data.map((item, index) => (
<View key={index}>
<Text>{item.name}</Text>
</View>
))}
</ScrollView>

解决方案三

如果您使您的平面列表水平(根据您的需要) ,然后也警告将消失

<ScrollView>
<FlatList
data={data}
keyExtractor={(item, index) => item.id + index.toString()}
horizontal={true}
/>
</ScrollView>

解决方案四

可以添加页眉和页脚组件

在 ListHeaderComponent 和 ListFooterComponent 中,您可以添加任何组件,因此不需要父 ScrollView

<FlatList
data={data}
keyExtractor={(item, index) => item.id + index.toString()}
ListHeaderComponent={headerComponent}
ListFooterComponent={footerComponent}
ListEmptyComponent={emptyComponent}
ItemSeparatorComponent={separator}
/>


// List components
const headerComponent = () => (
<View>
<Header/>
<Any/>
</View>
)
const footerComponent = () => (
<View>
<Footer/>
<Any/>
</View>
)
const emptyComponent = () => (
<View>
<EmptyView/>
<Any/>
</View>
)
const separator = () => (
<View style=\{\{height: 0.8, width: '100%', backgroundColor: '#fff'}} />
)

因此,当我在 <ScrollView>中使用一个基于选择器的组件时,我面临着同样的问题,而帮助我解决这个问题的一件事就是添加 keyboardShouldPersistTaps={true}<ScrollView>中作为道具。

这是我的代码片段。

<ScrollView keyboardShouldPersistTaps={true}>
<SelectionDD studentstatus={studentStatus}/>
<SearchableDD collegeNames={collegeNames} placeholder='University'/>
</ScrollView>

我也有同样的问题,只是通过删除 FlatList 周围的 ScrollView 来解决这个问题。因为默认情况下,FlatList 根据其呈现的内容长度提供滚动功能。

最好的方法是禁用该警告,因为有时 Flatlist需要在 ScrollView中。

更新 RN V0.63上文

YellowBox现在改为 LogBox

功能性的

import React, { useEffect } from 'react';
import { LogBox } from 'react-native';


useEffect(() => {
LogBox.ignoreLogs(['VirtualizedLists should never be nested']);
}, [])

类别为本

import React from 'react';
import { LogBox } from 'react-native';


componentDidMount() {
LogBox.ignoreLogs(['VirtualizedLists should never be nested']);
}

更新 RN V0.63

功能性的

import React, { useEffect } from 'react';
import { YellowBox } from 'react-native';


useEffect(() => {
YellowBox.ignoreWarnings(['VirtualizedLists should never be nested']);
}, [])

类别为本

import React from 'react';
import { YellowBox } from 'react-native';


componentDidMount() {
YellowBox.ignoreWarnings(['VirtualizedLists should never be nested']);
}

我的案子中,我需要将平面列表嵌套在 ScrollView 中,因为我正在使用 反应-本地-可拖动-平面列表来移动配料和步骤。

如果我们正确地阅读警告,它说我们应该使用另一个支持 VirtualizedList 的容器来嵌套我们的子平面列表。我所做的是:

 /* outside the component */
const emptyArry = []


/* render */
<FlatList
scrollEnabled={false}
horizontal
data={emptyArray}
ListEmptyComponent=(<DraggableList />)
/>

没有更多的警告,而且我认为这是警告所建议的模式。

在不隐藏 YellowBox 的情况下,您仍然可以在可滚动视图中实现可滚动视图。你可以使用 这个图书馆。它替换 React NativeScrollview 的默认值。

这可能会帮助某些人,确保您检查您的组件是如何嵌套的。从顶部组件移除 ScrollView 修复了这个问题。

我之所以遇到这个问题,是因为有两个组件基本上是这样嵌套的:

组件1

<ScrollView>
<OtherStuff />


<ListComponent />
</ScrollView>

我的第二个组件“ ListComponent”已经用 SafeAreaView 包装了一个平面列表。

<SafeAreaView style={styles.container}>
<FlatList
data={todoData}
renderItem={renderItem}
ItemSeparatorComponent={() => <View style={styles.separator} />}
keyExtractor={item => item.id.toString()}
/>
</SafeAreaView>

最后,我用 View 替换了第一个组件中的 ScrollView。

<ScrollView horizontal={false} style=\{\{width: '100%', height: '100%'}}>
<ScrollView horizontal={true} style=\{\{width: '100%', height: '100%'}}>
<FlatList ... />
</ScrollView>
</ScrollView>

可以将 horizontal=TruecontentContainerStyle=\{\{ width: '100%' }}添加到 ScrollView父级。

<ScrollView
style={styles.collaborators}
contentContainerStyle=\{\{ width: '100%' }} <--
horizontal <--
>
<FlatList
data={list?.slice(0, 10) || []}
keyExtractor={item => item.cc}
ItemSeparatorComponent={Separator}
renderItem={({ item }) => (
<Collaborator name={item.name} cc={item.cc} />
)}
/>
</ScrollView>

我在使用 scrollview 作为父视图时遇到了这个问题,并且将一个 SelectBox 嵌套在 response-national-multi-SelectBox 包中。我可以通过像下面这样加入 ListOptionProps = { nestedScrollEnable: true }}来解决这个问题:

<ScrollView>
<SelectBox
label="Select single"
options={serverData}
listOptionProps=\{\{nestedScrollEnabled: true}}
value={input.elementSelected}
onChange={event =>
inputHandlerLang('elementSelected', event, key)
}
hideInputFilter={false}
/>
</ScrollView>

错误仍然存在,但是在 SelectBox 中的滚动和在父 scrollview 中一样有效。我还必须使用 LogBox 来抑制错误。我不知道这样做是否有任何缺点,但我会尝试进一步测试。

下面的代码非常适合我禁用恼人的错误:

VirtualizedList 不应该嵌套在具有相同方向的普通 ScrollViews 中,因为它会破坏窗口和其他功能——而应该使用另一个支持 VirtualizedList 的容器。

反应原生 0.68.2

  <ScrollView horizontal={false} style=\{\{flex: 1}}>
<ScrollView
horizontal={true}
contentContainerStyle=\{\{width: '100%', height: '100%'}}>
<FlatList ... />
</ScrollView>
</ScrollView>

像这样使用 flatList ListHeaderComponentListFooterComponent:

<FlatList ListHeaderComponent={
<ScrollView
style={styles.yourstyle}
showsVerticalScrollIndicator={false}
>
<View style={styles.yourstyle}>
</View>
</ScrollView>
}
data={this.state.images}
renderItem={({ item, index }) => {
return (
<View
style={styles.yourstyle}
>
<Image
source=\{\{
uri: item,
}}
style={styles.yourstyle}
resizeMode={"contain"}
/>
<Text
numberOfLines={2}
ellipsizeMode="tail"
style={styles.yourstyle}
>
{item.name}
</Text>
</View>
);
}}
keyExtractor={({ name }, index) => index.toString()}
ListFooterComponent={
<View style={styles.yourstyle}></View>
}
/>

这对我有用(作为一个小黑客)。使用带有空数据和 null renderItem 道具的 FlatList,而不是使用 ScrollView

const emptyData = [];


const renderNullItem = () => null;


const App = () => {
const ListFooterComponent = (
<FlatList ... />
);


return (
<SafeAreaView>
<FlatList
data={emptyData}
renderItem={renderNullItem}
ListFooterComponent={ListFooterComponent}
/>
</SafeAreaView>
);
};

如果同时使用 ScrollView 和 FlatList,将得到不一致的滚动行为。 因此只需删除 ScrollView并在视图中使用平面列表。

<View flex={1}>
<FlatList
data={list}
renderItem={({ item }) => this.renderItem(item) }
keyExtractor={item => item.id}
/>
</View>

我有两个平面列表; 每个平面列表都有许多 Item,还有一个要折叠和展开的特性。 因此,我不能使用安全区域视图。

我看到了另一个解决方案,找到了新的方法。

我在核心组件中定义了一个平面列表(没有 Scrollview) ,并在 ListHeaderComponent 和 ListFooterComponent 中使用映射函数呈现每个平面列表。

 <View style=\{\{flex: 1}}>
<FlatList
style=\{\{backgroundColor: 'white'}}
refreshing={loading}
onRefresh={() => sample()}
ListHeaderComponent = {
<View>
{collapse/expandComponent}
{this.state.sample1&& content1.map((item, index) => this.renderList1(item,index))}
</View>
}
ListFooterComponent = {
<View>
{collapse/expandComponent}
{this.state.sample2 && content2.map((item, index) => this.renderlist2(item,index))}
</View>
}
/>
</View>
import React from 'react';
import { FlatList, ScrollViewProps } from 'react-native';


export const ScrollView: React.FC<ScrollViewProps> = props => {
return (
<FlatList
{...props}
data={[]}
renderItem={() => null}
ListHeaderComponent={() => (
<React.Fragment>{props.children}</React.Fragment>
)}
ListEmptyComponent={null}
keyExtractor={() => 'blank'}
/>
);
};

除了没有此错误之外,这将基本上与 ScrollView 一样工作。