2

我正在尝试构建一个具有左右轮播的反应原生屏幕,在轮播的每个面板中都有一个带有项目列表的垂直平面列表。在平面垂直平面列表中最多有 8-10 个轮播面板和 5-30 个项目,因此在渲染的最多项目中可能有 300 个,但通常是 100 个。

我正在调用 API 并每 2-3 秒检查一次服务器上的数据,并使用新数据设置组件中的状态。这目前有效,并且子组件中的数据数据会按应有的方式更新。

flatlist 中的每个项目都是可点击的,这会触发在页面中启动的模式弹出窗口。我遇到的问题是模式弹出窗口需要 4-5 秒才能出现和关闭。此外,当模态最终开始消失时,动画会变得生涩,并且深色背景层似乎在被移除时闪烁。

我首先尝试使用内置模式并且还使用了 react-native-modal 包,两者都是相同的。

我尝试使用 InteractionManager.runAfterInteractions 和 shouldComponentUpdate(nextProps, nextState) 来尝试阻止我的 api 调用,直到动画完成或阻止我的 isModalVisible 状态属性在我更改它时导致重新渲染。

下面的代码,任何帮助将不胜感激。

import {
    Text,
    Button,
    StyleSheet,
    View,   
    FlatList,   
    Dimensions,
    Image,
    Animated,
    SafeAreaView,
    TouchableHighlight,
    InteractionManager,
} from 'react-native';
import React from 'react';
import Title from './Title';
import CarouselMeeting from './CarouselMeeting';
import Modal from 'react-native-modal';
import Carousel from 'react-native-snap-carousel';

class MeetingDisplay extends React.Component {
constructor(props) {
    super(props);        
    this.state = {
        raceList: [],
        meetingList: [],
        meetingId: props.navigation.state.params.meetingId,
        currentIndex: 0,
        raceCount: 0,
        activeIndex: 0,
        isModalVisible: false,
    }
    this.refreshScreen = this.refreshScreen.bind(this)
}

componentDidMount() {
    InteractionManager.runAfterInteractions(() => {
    Promise.all([fetch('http://apicallurl?id' + this.state.meetingId), fetch('http://apicallurl?id' + this.state.meetingId)])
        .then(([res1, res2]) => {
            return Promise.all([res1.json(), res2.json()])
        })
        .then(([res1, res2]) => {
            this.setState({
                raceList: res1,
                meetingList: res2.Meets,

            })
        });
    this.interval = setInterval(() => this.updateRaceList(), 3000);
});
}

componentDidUpdate(prevProps, prevState) {
    InteractionManager.runAfterInteractions(() => {
    if (prevState.meetingId !== this.state.meetingId) {
        Promise.all([fetch('http://apicallurl?id' + this.state.meetingId), fetch('http://apicallurl?id' + this.state.meetingId)])

            .then(([res1, res2]) => {
                return Promise.all([res1.json(), res2.json()])
            })
            .then(([res1, res2]) => {
                this.setState({
                    raceList: res1,
                    meetingList: res2.Meets,

                })
            });
    }
});
}

async updateRaceList() {
    InteractionManager.runAfterInteractions(() => {
        fetch('http://apicallurl' + this.state.meetingId)
            .then((response) => response.json())
            .then((responseJson) => {
                this.setState({
                    raceList: responseJson,
                }, function () {

                });
            })
            .catch((error) => {
                console.error(error);
            });
    });
}

toggleModal = () => {
    InteractionManager.runAfterInteractions(() => {
        this.setState({ isModalVisible: !this.state.isModalVisible });
    });
};

shouldComponentUpdate(nextProps, nextState) {
    if(this.state.isModalVisible !== nextState.isModalVisible){
     this.setState({ isModalVisible: nextState.isModalVisible})
     return false;
    } else return true;
  }

render() {
    const peek = 20;
    const gutter = peek / 4;
    const cardWidth = Dimensions.get('window').width - gutter * 2 - peek * 2;
    const contentOffset = (Dimensions.get('window').width - (cardWidth + (gutter * 2))) / 2;

    return (
        <>
            <Title heading={this.state.raceList.VenueName} />                

            <SafeAreaView style={{ flex: 1, backgroundColor: 'rebeccapurple', paddingTop: 50, }}>
                <View style={{ flex: 1, flexDirection: 'row', justifyContent: 'center', }}>
                    <Carousel
                        layout={"default"}
                        useScrollView
                        ref={ref => this.Carousel = ref}
                        data={this.state.raceList.RaceList}
                        sliderWidth={cardWidth}
                        itemWidth={cardWidth - gutter * 2 - peek * 2}
                        onSnapToItem={index => this.setState({ activeIndex: index })}
                        renderItem={({ item }) => (
                            <Animated.View style={{
                                flex: 1,
                                paddingTop: 20,
                                width: cardWidth,                                    
                                margin: gutter,
                                backgroundColor: 'blue',
                                justifyContent: 'center',
                                alignItems: 'center',
                            }}>
                                <FlatList
                                    horizontal={false}
                                    showsVerticalScrollIndicator={true}
                                    legacyImplementation={false}

                                    data={item.runner_list}
                                    keyExtractor={(item, index) => index.toString()}
                                    renderItem={({ item }, index) =>
                                        <TouchableHighlight style={{ flex: 1, flexDirection: 'row' }} onPress={this.toggleModal} >
                                            <Image style={{ width: 50, height: 50 }} source={{ uri: item.imageurl }} />                                                
                                        </TouchableHighlight>}
                                >
                                </FlatList>
                            </Animated.View>
                        )}
                    />
                </View>
            </SafeAreaView>

            <Modal isVisible={this.state.isModalVisible}
                    backdropTransitionOutTiming={1}>
                <View style={{ flex: 1 }}>
                    <Text>Hello!</Text>
                    <Button title="Hide modal" onPress={this.toggleModal} />
                </View>
            </Modal>
        </>
    );
}
}

const styles = StyleSheet.create({
    centeredView: {
        flex: 1,
        justifyContent: "center",
    alignItems: "center",
    marginTop: 22
},
modalView: {
    margin: 20,
    backgroundColor: "white",
    borderRadius: 20,
    padding: 35,
    alignItems: "center",
    shadowColor: "#000",
    shadowOffset: {
        width: 0,
        height: 2
    },
    shadowOpacity: 0.25,
    shadowRadius: 3.84,
    elevation: 5
},
openButton: {
    backgroundColor: "#F194FF",
    borderRadius: 20,
    padding: 10,
    elevation: 2
},
textStyle: {
    color: "white",
    fontWeight: "bold",
    textAlign: "center"
},
modalText: {
    marginBottom: 15,
    textAlign: "center"
}
});

export default MeetingDisplay;

尝试 1

我有一个想法,这可能是我使用了一个名为“react-native-snap-carousel”的第三方轮播库,所以我试图用一个看起来很糟糕的滚动视图替换它,并让我所有的平面列表/项目都在其中呈现,但这确实没有改善仍然是 2-3 秒的弹出时间延迟。

尝试 2

我发现了一个叫做 react.purecomponent 的东西,它可能应该对状态/道具进行浅层比较,并且只在项目/状态实际发生变化时触发重新渲染,这可能意味着动画/ui线程导致问题停止。但没有更好的(在模拟器和设备上)在模态显示之前仍然长时间停顿

class MeetingDisplay extends React.PureComponent

尝试 4

通过在底部轮播下方的页面底部放置一个按钮来触发平面列表之外的模式,从而将平面列表排除在外。

....</View>
            </SafeAreaView>

            <Modal                    
                visible={this.state.isModalVisible}
                backdropTransitionOutTiming={1}
            >
                <View style={{ flex: 1 }}>
                    <Text>Hello!</Text>
                    <Button title="Hide modal" onPress={this.toggleModal} />
                </View>
            </Modal>
            <Button title="Show modal" onPress={this.toggleModal} />                
        </>
    );....

这导致没有改进或性能。那么还有什么导致这个问题。是因为间隔导致我的组件不断重新渲染吗?因此,必须有一种方法可以暂停我缺少的组件重新渲染。任何人?

4

1 回答 1

4

我遇到了同样的问题并通过将以下道具添加到模态来解决它

       useNativeDriver={true} 

请参考这个对我有帮助的答案, https://stackoverflow.com/a/62020910/10432212

于 2020-12-09T16:00:24.700 回答