performance - 在 React Native 中有效更新大型平面列表中的所有项目

标签 performance reactjs react-native react-native-flatlist

我正在使用react-native的CameraRoll编写一个图像选择器API 并将它们呈现在 FlatList 中里面CameraRollScreen成分。该组件需要一个名为 maxPhotos 的 prop ,比如说3,当用户选择了3张照片时,所有其他照片将被禁用(无法再选择),它看起来像这样(这就是我现在所拥有的,它可以工作,但性能不佳):

enter image description here

如您所见,当我选择了 3 张照片(这是限制)时,所有其他照片都被透明 View 覆盖(已禁用)。这不是性能问题,在 GIF 中看起来并非如此,但是当在真实设备上运行时,这个问题就不能再被忽视了。选择前两张照片不会导致任何延迟,但是,选择最后一张照片后,由于必须禁用所有其他照片,因此它会变得延迟。但我不知道如何才能禁用其他照片而不将它们一一禁用。这是我的图像选择器的代码:

由于每张图像都有不同的状态,我也将每张照片设为 PureComponentCameraRollImage具有以下状态:

{
    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 ShahRaphaMex .

如果我向下滚动得足够快,许多图像都不会渲染,当我到达它们时它们就会被渲染。这看起来是对的,为什么当它们不在屏幕上时还要渲染它们呢?我所做的是利用 FlatListonViewableItemsChanged:

<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 属性

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,我们不渲染图像,而是渲染一个大小相等的空 View ,你知道,以节省内存,当然,如果只有 100 个,这似乎并不重要照片:

render() {
    if (!this.state.viewable) {
        return (
            <View
                style={{
                    width={IMAGE_SIZE}
                    height={IMAGE_SIZE}
                }}
            />
        );
    }

    return (
        <Image
            ...
            ...
        />
    );
}

关于performance - 在 React Native 中有效更新大型平面列表中的所有项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48736903/

相关文章:

java - 散列长字符串进行比较还是比较两个字符串更快?

performance - Hibernate/JPA 与 JDBC 性能对比

javascript - 将自定义数据添加到 firebase 数据库

javascript - 更改子组件状态会更改父组件 props

reactjs - React Native "this._easing is not a function"与 v0.20

reactjs - 我如何在 React 路由器 v6 中用上下文包装 2 个路由

performance - SSAS 维度处理使基础度量未处理

performance - Haskell中的这个函数如何优化

javascript - 在 Sublime 和 atom 中注释 JSX 代码

css - React maskImage 内联不起作用 - 与 backgroundImage 相同的图像