diff --git a/src/components/Lists/PlanexGroups/GroupListAccordion.tsx b/src/components/Lists/PlanexGroups/GroupListAccordion.tsx index c746604..a6adf1b 100644 --- a/src/components/Lists/PlanexGroups/GroupListAccordion.tsx +++ b/src/components/Lists/PlanexGroups/GroupListAccordion.tsx @@ -18,8 +18,8 @@ */ import * as React from 'react'; -import { List, withTheme } from 'react-native-paper'; -import { FlatList, StyleSheet, View } from 'react-native'; +import { List, useTheme } from 'react-native-paper'; +import { FlatList, StyleSheet } from 'react-native'; import GroupListItem from './GroupListItem'; import AnimatedAccordion from '../../Animations/AnimatedAccordion'; import type { @@ -34,7 +34,6 @@ type PropsType = { onGroupPress: (data: PlanexGroupType) => void; onFavoritePress: (data: PlanexGroupType) => void; currentSearchString: string; - theme: ReactNativePaper.Theme; }; const LIST_ITEM_HEIGHT = 64; @@ -49,36 +48,22 @@ const styles = StyleSheet.create({ }, }); -class GroupListAccordion extends React.Component { - shouldComponentUpdate(nextProps: PropsType): boolean { - const { props } = this; - return ( - nextProps.currentSearchString !== props.currentSearchString || - nextProps.favorites.length !== props.favorites.length || - nextProps.item.content.length !== props.item.content.length - ); - } +function GroupListAccordion(props: PropsType) { + const theme = useTheme(); - getRenderItem = ({ item }: { item: PlanexGroupType }) => { - const { props } = this; - const onPress = () => { - props.onGroupPress(item); - }; - const onStarPress = () => { - props.onFavoritePress(item); - }; + const getRenderItem = ({ item }: { item: PlanexGroupType }) => { return ( f.id === item.id)} + onPress={() => props.onGroupPress(item)} + onStarPress={() => props.onFavoritePress(item)} /> ); }; - itemLayout = ( + const itemLayout = ( _data: Array | null | undefined, index: number ): { length: number; offset: number; index: number } => ({ @@ -87,57 +72,58 @@ class GroupListAccordion extends React.Component { index, }); - keyExtractor = (item: PlanexGroupType): string => item.id.toString(); + const keyExtractor = (item: PlanexGroupType): string => item.id.toString(); - render() { - const { props } = this; - const { item } = this.props; - var isFavorite = item.id === 0; - var isEmptyFavorite = isFavorite && props.favorites.length === 0; - return ( - - - isFavorite ? ( - - ) : undefined - } - unmountWhenCollapsed={!isFavorite} // Only render list if expanded for increased performance - opened={ - props.currentSearchString.length >= MIN_SEARCH_SIZE_EXPAND || - (isFavorite && !isEmptyFavorite) - } - enabled={!isEmptyFavorite} - > - + isFavorite ? ( + - - - ); - } + ) : undefined + } + unmountWhenCollapsed={!isFavorite} // Only render list if expanded for increased performance + opened={ + props.currentSearchString.length >= MIN_SEARCH_SIZE_EXPAND || + (isFavorite && !isEmptyFavorite) + } + enabled={!isEmptyFavorite} + > + + + ); } -export default withTheme(GroupListAccordion); +const propsEqual = (pp: PropsType, np: PropsType) => + pp.currentSearchString === np.currentSearchString && + pp.favorites.length === np.favorites.length && + pp.item.content.length === np.item.content.length && + pp.onFavoritePress === np.onFavoritePress; + +export default React.memo(GroupListAccordion, propsEqual); diff --git a/src/components/Lists/PlanexGroups/GroupListItem.tsx b/src/components/Lists/PlanexGroups/GroupListItem.tsx index e5180e7..93b0a5c 100644 --- a/src/components/Lists/PlanexGroups/GroupListItem.tsx +++ b/src/components/Lists/PlanexGroups/GroupListItem.tsx @@ -17,20 +17,19 @@ * along with Campus INSAT. If not, see . */ -import * as React from 'react'; -import { List, TouchableRipple, withTheme } from 'react-native-paper'; +import React, { useRef } from 'react'; +import { List, TouchableRipple, useTheme } from 'react-native-paper'; import * as Animatable from 'react-native-animatable'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import type { PlanexGroupType } from '../../../screens/Planex/GroupSelectionScreen'; import { StyleSheet, View } from 'react-native'; import { getPrettierPlanexGroupName } from '../../../utils/Utils'; -type PropsType = { - theme: ReactNativePaper.Theme; +type Props = { onPress: () => void; onStarPress: () => void; item: PlanexGroupType; - favorites: Array; + isFav: boolean; height: number; }; @@ -49,88 +48,51 @@ const styles = StyleSheet.create({ }, }); -class GroupListItem extends React.Component { - isFav: boolean; +function GroupListItem(props: Props) { + const theme = useTheme(); - starRef: { current: null | (Animatable.View & View) }; + const starRef = useRef(null); - constructor(props: PropsType) { - super(props); - this.starRef = React.createRef(); - this.isFav = this.isGroupInFavorites(props.favorites); - } - - shouldComponentUpdate(nextProps: PropsType): boolean { - const { favorites } = this.props; - const favChanged = favorites.length !== nextProps.favorites.length; - let newFavState = this.isFav; - if (favChanged) { - newFavState = this.isGroupInFavorites(nextProps.favorites); - } - const shouldUpdate = this.isFav !== newFavState; - this.isFav = newFavState; - return shouldUpdate; - } - - onStarPress = () => { - const { props } = this; - const ref = this.starRef; - if (ref.current && ref.current.rubberBand && ref.current.swing) { - if (this.isFav) { - ref.current.rubberBand(); - } else { - ref.current.swing(); - } - } - props.onStarPress(); - }; - - isGroupInFavorites(favorites: Array): boolean { - const { item } = this.props; - for (let i = 0; i < favorites.length; i += 1) { - if (favorites[i].id === item.id) { - return true; - } - } - return false; - } - - render() { - const { props } = this; - const { colors } = props.theme; - return ( - ( - - )} - right={(iconProps) => ( - - - - - - )} - style={{ - height: props.height, - ...styles.item, - }} - /> - ); - } + return ( + ( + + )} + right={(iconProps) => ( + + + + + + )} + style={{ + height: props.height, + ...styles.item, + }} + /> + ); } -export default withTheme(GroupListItem); +export default React.memo( + GroupListItem, + (pp: Props, np: Props) => + pp.isFav === np.isFav && pp.onStarPress === np.onStarPress +); diff --git a/src/screens/Planex/GroupSelectionScreen.tsx b/src/screens/Planex/GroupSelectionScreen.tsx index 43e257b..c4b70d1 100644 --- a/src/screens/Planex/GroupSelectionScreen.tsx +++ b/src/screens/Planex/GroupSelectionScreen.tsx @@ -17,7 +17,12 @@ * along with Campus INSAT. If not, see . */ -import React, { useEffect, useLayoutEffect, useState } from 'react'; +import React, { + useCallback, + useEffect, + useLayoutEffect, + useState, +} from 'react'; import { Platform } from 'react-native'; import i18n from 'i18n-js'; import { Searchbar } from 'react-native-paper'; @@ -142,39 +147,31 @@ function GroupSelectionScreen() { * * @param item The item to add/remove from favorites */ - const onListFavoritePress = (item: PlanexGroupType) => { - updateGroupFavorites(item); - }; + const onListFavoritePress = useCallback( + (group: PlanexGroupType) => { + const removeGroupFromFavorites = (g: PlanexGroupType) => { + setFavoriteGroups(favoriteGroups.filter((f) => f.id !== g.id)); + }; - /** - * Checks if the given group is in the favorites list - * - * @param group The group to check - * @returns {boolean} - */ - const isGroupInFavorites = (group: PlanexGroupType): boolean => { - let isFav = false; - favoriteGroups.forEach((favGroup: PlanexGroupType) => { - if (group.id === favGroup.id) { - isFav = true; + const addGroupToFavorites = (g: PlanexGroupType) => { + setFavoriteGroups([...favoriteGroups, g].sort(sortName)); + }; + + if (favoriteGroups.some((f) => f.id === group.id)) { + removeGroupFromFavorites(group); + } else { + addGroupToFavorites(group); } - }); - return isFav; - }; + }, + [favoriteGroups] + ); - /** - * Adds or removes the given group to the favorites list, depending on whether it is already in it or not. - * Favorites are then saved in user preferences - * - * @param group The group to add/remove to favorites - */ - const updateGroupFavorites = (group: PlanexGroupType) => { - if (isGroupInFavorites(group)) { - removeGroupFromFavorites(group); - } else { - addGroupToFavorites(group); - } - }; + useEffect(() => { + AsyncStorageManager.set( + AsyncStorageManager.PREFERENCES.planexFavoriteGroups.key, + favoriteGroups + ); + }, [favoriteGroups]); /** * Generates the dataset to be used in the FlatList. @@ -220,31 +217,6 @@ function GroupSelectionScreen() { return data; }; - /** - * Removes the given group from the favorites - * - * @param group The group to remove from the array - */ - const removeGroupFromFavorites = (group: PlanexGroupType) => { - setFavoriteGroups(favoriteGroups.filter((g) => g.id !== group.id)); - }; - - useEffect(() => { - AsyncStorageManager.set( - AsyncStorageManager.PREFERENCES.planexFavoriteGroups.key, - favoriteGroups - ); - }, [favoriteGroups]); - - /** - * Adds the given group to favorites - * - * @param group The group to add to the array - */ - const addGroupToFavorites = (group: PlanexGroupType) => { - setFavoriteGroups([...favoriteGroups, group].sort(sortName)); - }; - return ( readData(Urls.planex.groups)}