Add small performance improvement to album list

This commit is contained in:
Lei Nelissen
2020-07-05 22:25:47 +02:00
parent 80ff88c3b2
commit f5cfad326c
5 changed files with 55 additions and 30 deletions

View File

@@ -7,6 +7,7 @@ module.exports = {
'eslint:recommended', 'eslint:recommended',
'plugin:react/recommended', 'plugin:react/recommended',
'plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/eslint-recommended',
'plugin:react-hooks/recommended',
// "plugin:@typescript-eslint/recommended" // "plugin:@typescript-eslint/recommended"
], ],
globals: { globals: {
@@ -23,7 +24,8 @@ module.exports = {
}, },
plugins: [ plugins: [
'react', 'react',
'@typescript-eslint' '@typescript-eslint',
'react-hooks'
], ],
rules: { rules: {
indent: [ indent: [

View File

@@ -1,3 +1,12 @@
// import React from 'react';
// // if (process.env.NODE_ENV === 'development') {
// // const whyDidYouRender = require('@welldone-software/why-did-you-render');
// // whyDidYouRender(React, {
// // trackAllPureComponents: true,
// // });
// // }
import 'react-native-gesture-handler'; import 'react-native-gesture-handler';
import { AppRegistry } from 'react-native'; import { AppRegistry } from 'react-native';
import TrackPlayer from 'react-native-track-player'; import TrackPlayer from 'react-native-track-player';

14
package-lock.json generated
View File

@@ -1638,6 +1638,14 @@
"eslint-plugin-react-hooks": "^3.0.0", "eslint-plugin-react-hooks": "^3.0.0",
"eslint-plugin-react-native": "3.8.1", "eslint-plugin-react-native": "3.8.1",
"prettier": "^2.0.2" "prettier": "^2.0.2"
},
"dependencies": {
"eslint-plugin-react-hooks": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-3.0.0.tgz",
"integrity": "sha512-EjxTHxjLKIBWFgDJdhKKzLh5q+vjTFrqNZX36uIxWS4OfyXe5DawqPj3U5qeJ1ngLwatjzQnmR0Lz0J0YH3kxw==",
"dev": true
}
} }
}, },
"@react-native-community/eslint-plugin": { "@react-native-community/eslint-plugin": {
@@ -3943,9 +3951,9 @@
} }
}, },
"eslint-plugin-react-hooks": { "eslint-plugin-react-hooks": {
"version": "3.0.0", "version": "4.0.5",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-3.0.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.0.5.tgz",
"integrity": "sha512-EjxTHxjLKIBWFgDJdhKKzLh5q+vjTFrqNZX36uIxWS4OfyXe5DawqPj3U5qeJ1ngLwatjzQnmR0Lz0J0YH3kxw==", "integrity": "sha512-3YLSjoArsE2rUwL8li4Yxx1SUg3DQWp+78N3bcJQGWVZckcp+yeQGsap/MSq05+thJk57o+Ww4PtZukXGL02TQ==",
"dev": true "dev": true
}, },
"eslint-plugin-react-native": { "eslint-plugin-react-native": {

View File

@@ -59,6 +59,7 @@
"babel-jest": "^24.9.0", "babel-jest": "^24.9.0",
"babel-plugin-module-resolver": "^4.0.0", "babel-plugin-module-resolver": "^4.0.0",
"eslint": "^6.5.1", "eslint": "^6.5.1",
"eslint-plugin-react-hooks": "^4.0.5",
"jest": "^24.9.0", "jest": "^24.9.0",
"metro-react-native-babel-preset": "^0.58.0", "metro-react-native-babel-preset": "^0.58.0",
"react-test-renderer": "16.11.0", "react-test-renderer": "16.11.0",

View File

@@ -14,6 +14,7 @@ import AlbumImage, { AlbumItem } from './components/AlbumImage';
import { selectAlbumsByAlphabet, SectionedId } from 'store/music/selectors'; import { selectAlbumsByAlphabet, SectionedId } from 'store/music/selectors';
import AlphabetScroller from 'components/AlphabetScroller'; import AlphabetScroller from 'components/AlphabetScroller';
import { EntityId } from '@reduxjs/toolkit'; import { EntityId } from '@reduxjs/toolkit';
import styled from 'styled-components/native';
interface VirtualizedItemInfo { interface VirtualizedItemInfo {
section: SectionedId, section: SectionedId,
@@ -33,18 +34,31 @@ type VirtualizedSectionList = { _subExtractor: (index: number) => VirtualizedIte
function generateSection({ section }: { section: SectionedId }) { function generateSection({ section }: { section: SectionedId }) {
return ( return (
<SectionHeading label={section.label} /> <SectionHeading label={section.label} key={section.label} />
); );
} }
const SectionContainer = styled.View`
background-color: #f6f6f6;
border-bottom-color: #eee;
border-bottom-width: 1px;
height: 50px;
justify-content: center;
`;
const SectionText = styled.Text`
font-size: 24px;
font-weight: bold;
`;
class SectionHeading extends PureComponent<{ label: string }> { class SectionHeading extends PureComponent<{ label: string }> {
render() { render() {
const { label } = this.props; const { label } = this.props;
return ( return (
<View style={{ backgroundColor: '#f6f6f6', borderBottomColor: '#eee', borderBottomWidth: 1, height: 50, justifyContent: 'center' }}> <SectionContainer>
<Text style={{ fontSize: 24, fontWeight: 'bold'}}>{label}</Text> <SectionText>{label}</SectionText>
</View> </SectionContainer>
); );
} }
} }
@@ -57,6 +71,10 @@ interface GeneratedAlbumItemProps {
onPress: (id: string) => void; onPress: (id: string) => void;
} }
const HalfOpacity = styled.Text`
opacity: 0.5;
`;
class GeneratedAlbumItem extends PureComponent<GeneratedAlbumItemProps> { class GeneratedAlbumItem extends PureComponent<GeneratedAlbumItemProps> {
render() { render() {
const { id, imageUrl, name, artist, onPress } = this.props; const { id, imageUrl, name, artist, onPress } = this.props;
@@ -66,25 +84,13 @@ class GeneratedAlbumItem extends PureComponent<GeneratedAlbumItemProps> {
<AlbumItem> <AlbumItem>
<AlbumImage source={{ uri: imageUrl }} /> <AlbumImage source={{ uri: imageUrl }} />
<Text numberOfLines={1}>{name}</Text> <Text numberOfLines={1}>{name}</Text>
<Text numberOfLines={1} style={{ opacity: 0.5 }}>{artist}</Text> <HalfOpacity numberOfLines={1}>{artist}</HalfOpacity>
</AlbumItem> </AlbumItem>
</TouchableHandler> </TouchableHandler>
); );
} }
} }
// const getItemLayout: any = sectionListGetItemLayout({
// getItemHeight: (rowData, sectionIndex, rowIndex) => {
// console.log(sectionIndex, rowIndex, rowData);
// if (sectionIndex === 0) { return 0; }
// else if (rowIndex % 2 > 0) { return 0; }
// return 220;
// },
// getSectionHeaderHeight: () => 50,
// // getSeparatorHeight: () => 1 / PixelRatio.get(),
// // listHeaderHeight: 0,
// });
const Albums: React.FC = () => { const Albums: React.FC = () => {
// Retrieve data from store // Retrieve data from store
const { entities: albums } = useTypedSelector((state) => state.music.albums); const { entities: albums } = useTypedSelector((state) => state.music.albums);
@@ -121,6 +127,7 @@ const Albums: React.FC = () => {
const previousRows = data?.filter((row, i) => i < sectionIndex) const previousRows = data?.filter((row, i) => i < sectionIndex)
.reduce((sum, row) => sum + Math.ceil(row.data.length / 2), 0) || 0; .reduce((sum, row) => sum + Math.ceil(row.data.length / 2), 0) || 0;
// We must also calcuate the offset, total distance from the top of the // We must also calcuate the offset, total distance from the top of the
// screen. First off, we'll account for each sectionIndex that is shown up // screen. First off, we'll account for each sectionIndex that is shown up
// until now. This only includes the heading for the current section if the // until now. This only includes the heading for the current section if the
@@ -130,8 +137,6 @@ const Albums: React.FC = () => {
const itemOffset = 220 * (previousRows + currentRows); const itemOffset = 220 * (previousRows + currentRows);
const offset = headingOffset + itemOffset; const offset = headingOffset + itemOffset;
// console.log(index, sectionIndex, itemIndex, previousRows, currentRows, offset);
return { index, length, offset }; return { index, length, offset };
}, [listRef]); }, [listRef]);
@@ -141,14 +146,14 @@ const Albums: React.FC = () => {
const selectLetter = useCallback((sectionIndex: number) => { const selectLetter = useCallback((sectionIndex: number) => {
listRef.current?.scrollToLocation({ sectionIndex, itemIndex: 0, animated: false, }); listRef.current?.scrollToLocation({ sectionIndex, itemIndex: 0, animated: false, });
}, [listRef]); }, [listRef]);
const generateItem = ({ item, index, section }: { item: EntityId, index: number, section: SectionedId }) => { const generateItem = useCallback(({ item, index, section }: { item: EntityId, index: number, section: SectionedId }) => {
if (index % 2 === 1) { if (index % 2 === 1) {
return null; return <View key={item} />;
} }
const nextItem = section.data[index + 1]; const nextItem = section.data[index + 1];
return ( return (
<View style={{ flexDirection: 'row' }}> <View style={{ flexDirection: 'row' }} key={item}>
<GeneratedAlbumItem <GeneratedAlbumItem
id={item} id={item}
imageUrl={getImage(item as string)} imageUrl={getImage(item as string)}
@@ -167,7 +172,7 @@ const Albums: React.FC = () => {
} }
</View> </View>
); );
}; }, [albums, getImage, selectAlbum]);
// Retrieve data on mount // Retrieve data on mount
useEffect(() => { useEffect(() => {
@@ -175,7 +180,7 @@ const Albums: React.FC = () => {
if (!lastRefreshed || differenceInDays(lastRefreshed, new Date()) > ALBUM_CACHE_AMOUNT_OF_DAYS) { if (!lastRefreshed || differenceInDays(lastRefreshed, new Date()) > ALBUM_CACHE_AMOUNT_OF_DAYS) {
retrieveData(); retrieveData();
} }
}, []); });
return ( return (
<SafeAreaView> <SafeAreaView>
@@ -186,8 +191,8 @@ const Albums: React.FC = () => {
refreshing={isLoading} refreshing={isLoading}
onRefresh={retrieveData} onRefresh={retrieveData}
getItemLayout={getItemLayout} getItemLayout={getItemLayout}
keyExtractor={(d) => d as string}
ref={listRef} ref={listRef}
keyExtractor={(item, index) => `${item}_${index}`}
onScrollToIndexFailed={console.log} onScrollToIndexFailed={console.log}
renderSectionHeader={generateSection} renderSectionHeader={generateSection}
renderItem={generateItem} renderItem={generateItem}