Avoid unnecessary re-renders (#8673)

* Only set the height on the message component if it ends up being greater than the maxHeight to avoid re-renders

* Cache the teams theme to avoid unnecessary computation and re-renders

* useDidUpdate instead of useEffect to avoid setting the theme preference without need

* add comment on clear cache
This commit is contained in:
Elias Nahum 2025-03-13 21:28:48 +08:00 committed by GitHub
parent 4f9323ee25
commit d84a0afd3a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 58 additions and 26 deletions

View file

@ -67,7 +67,12 @@ const Message = ({currentUser, isHighlightWithoutNotificationLicensed, highlight
const blockStyles = getMarkdownBlockStyles(theme); const blockStyles = getMarkdownBlockStyles(theme);
const textStyles = getMarkdownTextStyles(theme); const textStyles = getMarkdownTextStyles(theme);
const onLayout = useCallback((event: LayoutChangeEvent) => setHeight(event.nativeEvent.layout.height), []); const onLayout = useCallback((event: LayoutChangeEvent) => {
const h = event.nativeEvent.layout.height;
if (h > maxHeight) {
setHeight(event.nativeEvent.layout.height);
}
}, [maxHeight]);
const onPress = () => setOpen(!open); const onPress = () => setOpen(!open);
const channelMentions = useMemo(() => { const channelMentions = useMemo(() => {

View file

@ -6,6 +6,7 @@ import React, {type ComponentType, createContext, useEffect, useState} from 'rea
import {Appearance} from 'react-native'; import {Appearance} from 'react-native';
import {Preferences} from '@constants'; import {Preferences} from '@constants';
import useDidUpdate from '@hooks/did_update';
import {queryThemePreferences} from '@queries/servers/preference'; import {queryThemePreferences} from '@queries/servers/preference';
import {observeCurrentTeamId} from '@queries/servers/system'; import {observeCurrentTeamId} from '@queries/servers/system';
import {setThemeDefaults, updateThemeIfNeeded} from '@utils/theme'; import {setThemeDefaults, updateThemeIfNeeded} from '@utils/theme';
@ -33,22 +34,43 @@ export function getDefaultThemeByAppearance(): Theme {
export const ThemeContext = createContext(getDefaultThemeByAppearance()); export const ThemeContext = createContext(getDefaultThemeByAppearance());
const {Consumer, Provider} = ThemeContext; const {Consumer, Provider} = ThemeContext;
const themeCache = new Map<string, Theme>();
let clearingThemeCache = false;
const clearThemeCache = () => {
if (clearingThemeCache) {
return;
}
clearingThemeCache = true;
themeCache.clear();
setTimeout(() => {
// We set this timeout to avoid clearing the cache multiple times
// this would happen as we have a Provider for each screen in the stack
// and when the themes changes we only want to invalidate the cache once
clearingThemeCache = false;
}, 300);
};
const getTheme = (teamId: string | undefined, themes: PreferenceModel[]): Theme => { const getTheme = (teamId: string | undefined, themes: PreferenceModel[]): Theme => {
if (teamId) { if (teamId && themeCache.has(teamId)) {
const teamTheme = themes.find((t) => t.name === teamId) || themes[0]; return themeCache.get(teamId)!;
if (teamTheme?.value) {
try {
const theme = setThemeDefaults(JSON.parse(teamTheme.value));
return theme;
} catch {
// no theme change
}
}
} }
const defaultTheme = getDefaultThemeByAppearance(); const newTheme = (() => {
if (teamId) {
const teamTheme = themes.find((t) => t.name === teamId) || themes[0];
if (teamTheme?.value) {
try {
return setThemeDefaults(JSON.parse(teamTheme.value));
} catch {
// no theme change
}
}
}
return getDefaultThemeByAppearance();
})();
return defaultTheme; themeCache.set(teamId || 'default', newTheme);
return newTheme;
}; };
const ThemeProvider = ({currentTeamId, children, themes}: Props) => { const ThemeProvider = ({currentTeamId, children, themes}: Props) => {
@ -69,7 +91,11 @@ const ThemeProvider = ({currentTeamId, children, themes}: Props) => {
updateThemeIfNeeded(theme); updateThemeIfNeeded(theme);
}, [theme]); }, [theme]);
useEffect(() => { useDidUpdate(() => {
clearThemeCache();
}, [themes]);
useDidUpdate(() => {
setTheme(getTheme(currentTeamId, themes)); setTheme(getTheme(currentTeamId, themes));
}, [currentTeamId, themes]); }, [currentTeamId, themes]);

View file

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React, {useCallback, useMemo, useState, useEffect} from 'react'; import React, {useCallback, useMemo, useState} from 'react';
import {savePreference} from '@actions/remote/preference'; import {savePreference} from '@actions/remote/preference';
import SettingContainer from '@components/settings/container'; import SettingContainer from '@components/settings/container';
@ -9,6 +9,7 @@ import {Preferences} from '@constants';
import {useServerUrl} from '@context/server'; import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme'; import {useTheme} from '@context/theme';
import useAndroidHardwareBackHandler from '@hooks/android_back_handler'; import useAndroidHardwareBackHandler from '@hooks/android_back_handler';
import useDidUpdate from '@hooks/did_update';
import {popTopScreen} from '@screens/navigation'; import {popTopScreen} from '@screens/navigation';
import CustomTheme from './custom_theme'; import CustomTheme from './custom_theme';
@ -30,16 +31,6 @@ const DisplayTheme = ({allowedThemeKeys, componentId, currentTeamId, currentUser
const close = () => popTopScreen(componentId); const close = () => popTopScreen(componentId);
useEffect(() => {
const differentTheme = theme.type?.toLowerCase() !== newTheme?.toLowerCase();
if (!differentTheme) {
close();
return;
}
setThemePreference();
}, [newTheme]);
const setThemePreference = useCallback(() => { const setThemePreference = useCallback(() => {
const allowedTheme = allowedThemeKeys.find((tk) => tk === newTheme); const allowedTheme = allowedThemeKeys.find((tk) => tk === newTheme);
const themeJson = Preferences.THEMES[allowedTheme as ThemeKey] || initialTheme; const themeJson = Preferences.THEMES[allowedTheme as ThemeKey] || initialTheme;
@ -51,7 +42,17 @@ const DisplayTheme = ({allowedThemeKeys, componentId, currentTeamId, currentUser
value: JSON.stringify(themeJson), value: JSON.stringify(themeJson),
}; };
savePreference(serverUrl, [pref]); savePreference(serverUrl, [pref]);
}, [allowedThemeKeys, currentTeamId, theme.type, serverUrl, newTheme]); }, [allowedThemeKeys, initialTheme, currentTeamId, currentUserId, serverUrl, newTheme]);
useDidUpdate(() => {
const differentTheme = theme.type?.toLowerCase() !== newTheme?.toLowerCase();
if (!differentTheme) {
close();
return;
}
setThemePreference();
}, [close, newTheme, setThemePreference, theme.type]);
const onAndroidBack = () => { const onAndroidBack = () => {
setThemePreference(); setThemePreference();