How to efficiently render different views in FlatList

 ̄綄美尐妖づ 提交于 2019-12-10 11:59:03

问题


I am creating a calendar component that contains 2 Views with FlatList. Each View is associated with a different data source. Specifically, View 1 will display all days in a month where View 2 will display all days in a week.

Link to video demo, button of year is for view switching

For simplicity, each month/week view is represented by a date as an array item in a bigger array.

For example, each item in the month array is a unique data in a month. An array of size 12 will have 12 unique dates of each month. Each Date() object will then be passed down to my children components for a proper rendering. The result of this particular month array is a list of complete calendar of 12 months.

Note: To improve performance, my child component will only render the current month -1, current month, and current month +1, all other months will be rendered conditionally as soon as user scrolls away.

[
    ...
    Wed May 30 2018 21:51:47 GMT+0800 (+08),
    Sat Jun 30 2018 21:51:47 GMT+0800 (+08),
    Mon Jul 30 2018 21:51:47 GMT+0800 (+08),
    ...
]

The same applies to View 2, which is the week view. Except that my data is now contained unique days in a week for all weeks in a month.

Before I implement this approach, I tried to hack something out from my child component. For instance, when users click a button to switch view, it would pass down a prop, with the current Date() object to notify the children as to conditionally render a different view. This is basically switching between a View and a FlatList. Nested FlatList was a bad idea as both lists are scrolling towards the same direction.

Not to mention that my calendar component involve jumping to selected month. Another solution I had implemented is to use only month array for both views by hacking out the indexes from keyExtractor. For example, I would reindex the list as soon as user jumps from one month to another but soon I realized this is an anti-pattern an only causes more problem.

Note: I have implemented shouldComponentUpdate in all my children components. So scrolling in just 1 View should not be a problem, size of the data source will not affect at all because only the changes happen to the -1, 0, and +1 month will be reflected. The bottleneck of my component is only happening when switching views.

Lost story short, now I have resorted to the current solution where I have 2 different data source (array) for 2 different Views in a FlatList. I have a button for the user to switch between modes. The problem is it takes some time for switching mode (setting states) as every time it re-instantiates the view by calling this prop onViewableItemsChanged from FlatList, which involve some complex calculation. Is there a better approach?

Code (Parent)

renderCalendarList = () => {
        return (
            <FlatList
                pageSize={1}
                horizontal={true}
                pagingEnabled={true}
                scrollEnabled={true}
                keyExtractor={this.keyExtractor}
                ref={(c) => this.calendarList = c}
                getItemLayout={this.getItemLayout}
                renderItem={this.renderCalendarComponent}
                onViewableItemsChanged={this.onViewableItemsChanged}
                data={(this.state.weekMode ? this.state.weekRows : this.state.rows)} />
        )
    }

switchView = () => {
        this.setState({ weekMode: !this.state.weekMode });

        // next week is going to be month view
        if (this.state.weekMode) {
            const mYClone = this.state.monthYears.slice(0);
            const selectedDay = DateUtils.formatDateInMY(this.props.selectedDay);

            for (let i = 0; i < mYClone.length; i++) {
                if (selectedDay === mYClone[i]) {
                    this.setState({ currentMonth: this.props.selectedDay })
                    this.calendarList.scrollToIndex({ animated: false, index: i });
                }
            }
        } else {             // next week is going to be week view
            const rowClone = this.state.weekRows.slice(0);

            for (let i = 0; i < rowClone.length; i++) {
                if (isSameWeek(rowClone[i], this.props.selectedDay)) {
                    this.setState({ currentMonth: rowClone[i] });
                    this.calendarList.scrollToIndex({ animated: false, index: i });
                }
            }
        }
    }

Code (Children)

render() {
        const { container, monthContainer, weekContainer } = styles;
        const { currentMonth, firstDay, style, weekMode } = this.props;
        if (!weekMode) {
            const days = DateUtils.populateMonth(currentMonth, firstDay);
            const weeks = [];
            while (days.length) {
                weeks.push(this.renderWeek(days.splice(0, 7), weeks.length));
            }
            return (
                <View style={[container, style]}>
                    <View style={[monthContainer]}>{weeks}</View>
                </View>
            )
        } else {
            const startDay = subDays(currentMonth, 3); // focus on middle
            const endDay = addDays(startDay, 6)
            const days = eachDay(startDay, endDay, 1);
            const daysToRender = [];
            days.forEach((day, dayID) => {
                daysToRender.push(this.renderDay(day, dayID))
            });
            return (
                <View style={style}>
                    <View style={[weekContainer]}>
                        {daysToRender}
                    </View>
                </View>
            )
        }
    }

Link to video demo, button of year is for view switching

来源:https://stackoverflow.com/questions/51115843/how-to-efficiently-render-different-views-in-flatlist

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!