在本机反应中有效地更新大型平面列表中的所有项目
Efficiently update all items in large flat list in react native
我正在使用 react-native 的 CameraRoll
API 编写一个图像选择器,并在 CameraRollScreen
组件内的 FlatList
中渲染它们。这个组件有一个属性叫maxPhotos
,比如说3,当用户选择了3张照片时,所有其他照片将被禁用(不能再被选择),它看起来像这样(这是我现在拥有的,它有效,但性能不佳):
如您所见,当我选择了 3 张照片(这是限制)时,所有其他照片都被透明视图覆盖(禁用)。这不是高性能的,在 GIF 中似乎不是这样,但是当 运行 在真实设备上时,这个问题就不能再被忽略了。选择前 2 张照片不会造成任何延迟,但是,在选择最后一张照片时,由于必须禁用所有其他照片,它会变得延迟。但是我不知道我还能如何禁用其他照片而不是将它们 1 1 1 地禁用。这是我的图像选择器的代码:
由于每张图片都有不同的状态,我也将每张照片制作成一个名为 CameraRollImage
的 PureComponent
,具有以下状态:
{
uri: '',
index: -1 // if not selected, it's -1, if selected, it denotes
// the position of the photo in the 'selectedPhotos'
// array
disabled: false // Whether it should be disabled
}
CameraRollImage
分量:
class CameraRollImage extends PureComponent {
constructor(props) {
super(props);
this.state = {
uri: '',
index: -1,
disabled: false
};
this.onSelectPhoto = this.onSelectPhoto.bind(this);
}
componentWillMount() {
const { uri, index, disabled } = this.props;
this.setState({ uri, index, disabled });
}
componentWillReceiveProps(nextProps) {
const { uri, index, disabled } = nextProps;
this.setState({ uri, index, disabled });
}
onSelectPhoto() {
const { uri, index } = this.state;
this.props.onSelectPhoto({ uri, index });
// 'onSelectPhoto' is a method passed down to each photo
// from 'CameraRollScreen' component
}
render() {
const { uri, index, disabled } = this.state;
return (
<View style={{ ... }}>
<TouchableOpacity
disabled={disabled}
onPress={this.onSelectPhoto}
>
<Image
source={{ uri }}
style={{ ... }}
/>
</TouchableOpacity>
// If disabled, render a transparent view that covers the photo
{disabled && <View
style={{
position: 'absolute',
backgroundColor: 'rgba(0, 0, 0, 0.75)',
width: ... height: ...
}}
/>}
// render the index here
</View>
);
}
}
export default CameraRollImage;
然后,在CameraRollScreen
组件中:
class CameraRollScreen extends Component {
constructor(props) {
super(props);
this.state = {
allPhotos: [], // all photos in camera roll
selectedPhotos: []
};
this.onSelectPhoto = this.onSelectPhoto.bind(this);
this.renderPhoto = this.renderPhoto.bind(this);
}
componentWillMount() {
// Access the photo library to grab all photos
// using 'CameraRoll' API then push all photos
// to 'allPhotos' property of 'this.state'
}
onSelectPhoto({ uri, index }) {
let { selectedPhotos } = { ...this.state };
if (index === -1) {
// this means that this photo is not selected
// and we should add it to 'selectedPhotos' array
selectedPhotos.push(uri);
} else {
_.pullAt(selectedPhotos, index);
}
this.setState({ selectedPhotos });
}
renderPhoto({ item }) {
// item is the uri of the photo
const { selectedPhotos } = this.state;
const index = _.indexOf(selectedPhotos, item);
// A photo should be disabled when reach the limit &&
// it's not selected (index === -1)
return (
<CameraRollImage
uri={item}
index={index}
onSelectPhoto={this.onSelectPhoto}
disabled={index === -1 && selectedPhotos.length >= 3}
/>
);
}
render() {
const { allPhotos } = this.state;
return (
<FlatList
data={allPhotos}
extraData={this.state}
...
...
numColumns={3}
renderItem={this.renderPhoto}
/>
);
}
}
export default CameraRollScreen;
我的照片库中只有 100 张照片,这已经造成了延迟,很多人的照片比我多得多,这样会造成灾难,但我应该如何更新 FlatList
?或者,我应该使用 FlatList
吗?
找到解决方案,感谢 Pir Shukarullah Shah and RaphaMex。
如果我向下滚动得足够快,很多图像都没有渲染,当我到达它们时它们正在渲染。这似乎是正确的,为什么当它们不在屏幕上时仍然渲染它们?我所做的是我利用了 onViewableItemsChanged
of FlatList
:
<FlatList
...
...
keyExtractor={(item) => item} // This is important!!!
onViewableItemsChanged={this.onViewablePhotosChanged}
initialNumberToRender={Math.ceil(SCREEN_HEIGHT / IMAGE_SIZE) * 3}
...
/>
然后,onViewablePhotosChanged
方法:
onViewablePhotosChanged({ viewableItems }) {
let viewablePhotos = [];
viewableItems.forEach((item) => viewablePhotos.push(item.key));
this.setState({ viewablePhotos });
// Here, every object in 'viewableItems' has a key, which
// is the key you provided in 'keyExtractor={(item) => ...}',
// I used the 'uri' of each photo as the key, that's why
// I am pushing viewable photos' uri's to 'viewablePhotos' array
}
最后,修改renderPhoto
函数传递一个viewable
prop
renderPhoto({ item }) {
...
...
return (
<CameraRollImage
...
...
viewable={_.include(this.state.viewablePhotos, item)}
/>
);
}
然后,在 CameraRollImage
组件中,我们渲染图像的地方,有一个名为 viewable
的 prop,如果 viewable === false
,我们根本不更新它:
componentWillReceiveProps(nextProps) {
const { ..., ..., viewable } = nextProps;
if (!viewable) {
this.setState({ viewable: false });
return;
}
...
...
}
更好!!!如果 viewable
为 false,我们不渲染图像,而是渲染一个大小相等的空视图,你知道,以节省内存,当然,如果只有 100 张照片,这似乎并不重要:
render() {
if (!this.state.viewable) {
return (
<View
style={{
width={IMAGE_SIZE}
height={IMAGE_SIZE}
}}
/>
);
}
return (
<Image
...
...
/>
);
}
我正在使用 react-native 的 CameraRoll
API 编写一个图像选择器,并在 CameraRollScreen
组件内的 FlatList
中渲染它们。这个组件有一个属性叫maxPhotos
,比如说3,当用户选择了3张照片时,所有其他照片将被禁用(不能再被选择),它看起来像这样(这是我现在拥有的,它有效,但性能不佳):
如您所见,当我选择了 3 张照片(这是限制)时,所有其他照片都被透明视图覆盖(禁用)。这不是高性能的,在 GIF 中似乎不是这样,但是当 运行 在真实设备上时,这个问题就不能再被忽略了。选择前 2 张照片不会造成任何延迟,但是,在选择最后一张照片时,由于必须禁用所有其他照片,它会变得延迟。但是我不知道我还能如何禁用其他照片而不是将它们 1 1 1 地禁用。这是我的图像选择器的代码:
由于每张图片都有不同的状态,我也将每张照片制作成一个名为 CameraRollImage
的 PureComponent
,具有以下状态:
{
uri: '',
index: -1 // if not selected, it's -1, if selected, it denotes
// the position of the photo in the 'selectedPhotos'
// array
disabled: false // Whether it should be disabled
}
CameraRollImage
分量:
class CameraRollImage extends PureComponent {
constructor(props) {
super(props);
this.state = {
uri: '',
index: -1,
disabled: false
};
this.onSelectPhoto = this.onSelectPhoto.bind(this);
}
componentWillMount() {
const { uri, index, disabled } = this.props;
this.setState({ uri, index, disabled });
}
componentWillReceiveProps(nextProps) {
const { uri, index, disabled } = nextProps;
this.setState({ uri, index, disabled });
}
onSelectPhoto() {
const { uri, index } = this.state;
this.props.onSelectPhoto({ uri, index });
// 'onSelectPhoto' is a method passed down to each photo
// from 'CameraRollScreen' component
}
render() {
const { uri, index, disabled } = this.state;
return (
<View style={{ ... }}>
<TouchableOpacity
disabled={disabled}
onPress={this.onSelectPhoto}
>
<Image
source={{ uri }}
style={{ ... }}
/>
</TouchableOpacity>
// If disabled, render a transparent view that covers the photo
{disabled && <View
style={{
position: 'absolute',
backgroundColor: 'rgba(0, 0, 0, 0.75)',
width: ... height: ...
}}
/>}
// render the index here
</View>
);
}
}
export default CameraRollImage;
然后,在CameraRollScreen
组件中:
class CameraRollScreen extends Component {
constructor(props) {
super(props);
this.state = {
allPhotos: [], // all photos in camera roll
selectedPhotos: []
};
this.onSelectPhoto = this.onSelectPhoto.bind(this);
this.renderPhoto = this.renderPhoto.bind(this);
}
componentWillMount() {
// Access the photo library to grab all photos
// using 'CameraRoll' API then push all photos
// to 'allPhotos' property of 'this.state'
}
onSelectPhoto({ uri, index }) {
let { selectedPhotos } = { ...this.state };
if (index === -1) {
// this means that this photo is not selected
// and we should add it to 'selectedPhotos' array
selectedPhotos.push(uri);
} else {
_.pullAt(selectedPhotos, index);
}
this.setState({ selectedPhotos });
}
renderPhoto({ item }) {
// item is the uri of the photo
const { selectedPhotos } = this.state;
const index = _.indexOf(selectedPhotos, item);
// A photo should be disabled when reach the limit &&
// it's not selected (index === -1)
return (
<CameraRollImage
uri={item}
index={index}
onSelectPhoto={this.onSelectPhoto}
disabled={index === -1 && selectedPhotos.length >= 3}
/>
);
}
render() {
const { allPhotos } = this.state;
return (
<FlatList
data={allPhotos}
extraData={this.state}
...
...
numColumns={3}
renderItem={this.renderPhoto}
/>
);
}
}
export default CameraRollScreen;
我的照片库中只有 100 张照片,这已经造成了延迟,很多人的照片比我多得多,这样会造成灾难,但我应该如何更新 FlatList
?或者,我应该使用 FlatList
吗?
找到解决方案,感谢 Pir Shukarullah Shah and RaphaMex。
如果我向下滚动得足够快,很多图像都没有渲染,当我到达它们时它们正在渲染。这似乎是正确的,为什么当它们不在屏幕上时仍然渲染它们?我所做的是我利用了 onViewableItemsChanged
of FlatList
:
<FlatList
...
...
keyExtractor={(item) => item} // This is important!!!
onViewableItemsChanged={this.onViewablePhotosChanged}
initialNumberToRender={Math.ceil(SCREEN_HEIGHT / IMAGE_SIZE) * 3}
...
/>
然后,onViewablePhotosChanged
方法:
onViewablePhotosChanged({ viewableItems }) {
let viewablePhotos = [];
viewableItems.forEach((item) => viewablePhotos.push(item.key));
this.setState({ viewablePhotos });
// Here, every object in 'viewableItems' has a key, which
// is the key you provided in 'keyExtractor={(item) => ...}',
// I used the 'uri' of each photo as the key, that's why
// I am pushing viewable photos' uri's to 'viewablePhotos' array
}
最后,修改renderPhoto
函数传递一个viewable
prop
renderPhoto({ item }) {
...
...
return (
<CameraRollImage
...
...
viewable={_.include(this.state.viewablePhotos, item)}
/>
);
}
然后,在 CameraRollImage
组件中,我们渲染图像的地方,有一个名为 viewable
的 prop,如果 viewable === false
,我们根本不更新它:
componentWillReceiveProps(nextProps) {
const { ..., ..., viewable } = nextProps;
if (!viewable) {
this.setState({ viewable: false });
return;
}
...
...
}
更好!!!如果 viewable
为 false,我们不渲染图像,而是渲染一个大小相等的空视图,你知道,以节省内存,当然,如果只有 100 张照片,这似乎并不重要:
render() {
if (!this.state.viewable) {
return (
<View
style={{
width={IMAGE_SIZE}
height={IMAGE_SIZE}
}}
/>
);
}
return (
<Image
...
...
/>
);
}