forked from brandon.presley/Massive
Improve performance of app - 1.182 🚀
The App.tsx had a bunch of separate useState calls which would cause unneccesary re-renders of the entire app. This became apparent after adding the global progress bar, since it caused even more re-renders to the point of being unusable.
This commit is contained in:
parent
49646c3107
commit
6950cd04f4
114
App.tsx
114
App.tsx
|
@ -1,27 +1,22 @@
|
||||||
import {
|
import {
|
||||||
|
NavigationContainer,
|
||||||
DarkTheme as NavigationDarkTheme,
|
DarkTheme as NavigationDarkTheme,
|
||||||
DefaultTheme as NavigationDefaultTheme,
|
DefaultTheme as NavigationDefaultTheme,
|
||||||
NavigationContainer,
|
|
||||||
} from "@react-navigation/native";
|
} from "@react-navigation/native";
|
||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import { useColorScheme } from "react-native";
|
import { useColorScheme } from "react-native";
|
||||||
import {
|
import {
|
||||||
MD3DarkTheme as PaperDarkTheme,
|
MD3DarkTheme as PaperDarkTheme,
|
||||||
MD3LightTheme as PaperDefaultTheme,
|
MD3LightTheme as PaperDefaultTheme,
|
||||||
ProgressBar,
|
|
||||||
Provider as PaperProvider,
|
Provider as PaperProvider,
|
||||||
Snackbar,
|
|
||||||
} from "react-native-paper";
|
} from "react-native-paper";
|
||||||
import MaterialIcon from "react-native-vector-icons/MaterialCommunityIcons";
|
import MaterialIcon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||||
|
import AppSnack from "./AppSnack";
|
||||||
import AppStack from "./AppStack";
|
import AppStack from "./AppStack";
|
||||||
|
import TimerProgress from "./TimerProgress";
|
||||||
import { AppDataSource } from "./data-source";
|
import { AppDataSource } from "./data-source";
|
||||||
import { settingsRepo } from "./db";
|
import { settingsRepo } from "./db";
|
||||||
import { emitter } from "./emitter";
|
|
||||||
import { TickEvent } from "./TimerPage";
|
|
||||||
import { TOAST } from "./toast";
|
|
||||||
import { ThemeContext } from "./use-theme";
|
import { ThemeContext } from "./use-theme";
|
||||||
import Settings from "./settings";
|
|
||||||
import { MARGIN } from "./constants";
|
|
||||||
|
|
||||||
export const CombinedDefaultTheme = {
|
export const CombinedDefaultTheme = {
|
||||||
...NavigationDefaultTheme,
|
...NavigationDefaultTheme,
|
||||||
|
@ -43,61 +38,54 @@ export const CombinedDarkTheme = {
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const phoneTheme = useColorScheme();
|
const phoneTheme = useColorScheme();
|
||||||
const [initialized, setInitialized] = useState(false);
|
|
||||||
const [snackbar, setSnackbar] = useState("");
|
|
||||||
const [appTheme, setAppTheme] = useState("system");
|
|
||||||
const [progress, setProgress] = useState(0);
|
|
||||||
const [settings, setSettings] = useState<Settings>();
|
|
||||||
|
|
||||||
const [lightColor, setLightColor] = useState<string>(
|
const [appSettings, setAppSettings] = useState({
|
||||||
CombinedDefaultTheme.colors.primary
|
startup: undefined,
|
||||||
);
|
theme: "system",
|
||||||
|
lightColor: CombinedDefaultTheme.colors.primary,
|
||||||
|
darkColor: CombinedDarkTheme.colors.primary,
|
||||||
|
});
|
||||||
|
|
||||||
const [darkColor, setDarkColor] = useState<string>(
|
console.log("Rerendered App");
|
||||||
CombinedDarkTheme.colors.primary
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
if (!AppDataSource.isInitialized) await AppDataSource.initialize();
|
if (!AppDataSource.isInitialized) await AppDataSource.initialize();
|
||||||
const gotSettings = await settingsRepo.findOne({ where: {} });
|
const gotSettings = await settingsRepo.findOne({ where: {} });
|
||||||
console.log({ gotSettings });
|
console.log({ gotSettings });
|
||||||
setSettings(gotSettings);
|
setAppSettings({
|
||||||
setAppTheme(gotSettings.theme);
|
startup: gotSettings.startup,
|
||||||
if (gotSettings.lightColor) setLightColor(gotSettings.lightColor);
|
theme: gotSettings.theme,
|
||||||
if (gotSettings.darkColor) setDarkColor(gotSettings.darkColor);
|
lightColor: gotSettings.lightColor,
|
||||||
setInitialized(true);
|
darkColor: gotSettings.darkColor,
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const descriptions = [
|
|
||||||
emitter.addListener(TOAST, ({ value }: { value: string }) => {
|
|
||||||
setSnackbar(value);
|
|
||||||
}),
|
|
||||||
emitter.addListener("tick", (event: TickEvent) => {
|
|
||||||
setProgress((Number(event.minutes) * 60 + Number(event.seconds)) / 210);
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
return () => descriptions.forEach((description) => description.remove());
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const paperTheme = useMemo(() => {
|
const paperTheme = useMemo(() => {
|
||||||
const darkTheme = lightColor
|
const darkTheme = appSettings.lightColor
|
||||||
? {
|
? {
|
||||||
...CombinedDarkTheme,
|
...CombinedDarkTheme,
|
||||||
colors: { ...CombinedDarkTheme.colors, primary: darkColor },
|
colors: {
|
||||||
|
...CombinedDarkTheme.colors,
|
||||||
|
primary: appSettings.darkColor,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
: CombinedDarkTheme;
|
: CombinedDarkTheme;
|
||||||
const lightTheme = lightColor
|
const lightTheme = appSettings.lightColor
|
||||||
? {
|
? {
|
||||||
...CombinedDefaultTheme,
|
...CombinedDefaultTheme,
|
||||||
colors: { ...CombinedDefaultTheme.colors, primary: lightColor },
|
colors: {
|
||||||
|
...CombinedDefaultTheme.colors,
|
||||||
|
primary: appSettings.lightColor,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
: CombinedDefaultTheme;
|
: CombinedDefaultTheme;
|
||||||
let value = phoneTheme === "dark" ? darkTheme : lightTheme;
|
let value = phoneTheme === "dark" ? darkTheme : lightTheme;
|
||||||
if (appTheme === "dark") value = darkTheme;
|
if (appSettings.theme === "dark") value = darkTheme;
|
||||||
else if (appTheme === "light") value = lightTheme;
|
else if (appSettings.theme === "light") value = lightTheme;
|
||||||
return value;
|
return value;
|
||||||
}, [phoneTheme, appTheme, lightColor, darkColor]);
|
}, [phoneTheme, appSettings]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PaperProvider
|
<PaperProvider
|
||||||
|
@ -105,46 +93,26 @@ const App = () => {
|
||||||
settings={{ icon: (props) => <MaterialIcon {...props} /> }}
|
settings={{ icon: (props) => <MaterialIcon {...props} /> }}
|
||||||
>
|
>
|
||||||
<NavigationContainer theme={paperTheme}>
|
<NavigationContainer theme={paperTheme}>
|
||||||
{initialized && (
|
{appSettings.startup !== undefined && (
|
||||||
<ThemeContext.Provider
|
<ThemeContext.Provider
|
||||||
value={{
|
value={{
|
||||||
theme: appTheme,
|
theme: appSettings.theme,
|
||||||
setTheme: setAppTheme,
|
setTheme: (theme) => setAppSettings({ ...appSettings, theme }),
|
||||||
lightColor,
|
lightColor: appSettings.lightColor,
|
||||||
setLightColor,
|
setLightColor: (color) =>
|
||||||
darkColor,
|
setAppSettings({ ...appSettings, lightColor: color }),
|
||||||
setDarkColor,
|
darkColor: appSettings.darkColor,
|
||||||
|
setDarkColor: (color) =>
|
||||||
|
setAppSettings({ ...appSettings, darkColor: color }),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppStack settings={settings} />
|
<AppStack startup={appSettings.startup} />
|
||||||
</ThemeContext.Provider>
|
</ThemeContext.Provider>
|
||||||
)}
|
)}
|
||||||
</NavigationContainer>
|
</NavigationContainer>
|
||||||
|
|
||||||
<Snackbar
|
<AppSnack textColor={paperTheme.colors.background} />
|
||||||
duration={3000}
|
<TimerProgress />
|
||||||
onDismiss={() => setSnackbar("")}
|
|
||||||
visible={!!snackbar}
|
|
||||||
action={{
|
|
||||||
label: "Close",
|
|
||||||
onPress: () => setSnackbar(""),
|
|
||||||
textColor: paperTheme.colors.background,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{snackbar}
|
|
||||||
</Snackbar>
|
|
||||||
|
|
||||||
{progress > 0 && (
|
|
||||||
<ProgressBar
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
bottom: MARGIN / 2,
|
|
||||||
left: MARGIN,
|
|
||||||
right: MARGIN,
|
|
||||||
}}
|
|
||||||
progress={progress}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</PaperProvider>
|
</PaperProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { createDrawerNavigator } from "@react-navigation/drawer";
|
import { createDrawerNavigator } from "@react-navigation/drawer";
|
||||||
|
import { StackScreenProps } from "@react-navigation/stack";
|
||||||
import { IconButton } from "react-native-paper";
|
import { IconButton } from "react-native-paper";
|
||||||
import { DrawerParams } from "./drawer-param-list";
|
|
||||||
import ExerciseList from "./ExerciseList";
|
import ExerciseList from "./ExerciseList";
|
||||||
import GraphsList from "./GraphsList";
|
import GraphsList from "./GraphsList";
|
||||||
import InsightsPage from "./InsightsPage";
|
import InsightsPage from "./InsightsPage";
|
||||||
|
@ -8,20 +8,19 @@ import PlanList from "./PlanList";
|
||||||
import SetList from "./SetList";
|
import SetList from "./SetList";
|
||||||
import SettingsPage from "./SettingsPage";
|
import SettingsPage from "./SettingsPage";
|
||||||
import TimerPage from "./TimerPage";
|
import TimerPage from "./TimerPage";
|
||||||
import useDark from "./use-dark";
|
|
||||||
import WeightList from "./WeightList";
|
import WeightList from "./WeightList";
|
||||||
import Settings from "./settings";
|
import { DrawerParams } from "./drawer-param-list";
|
||||||
import { StackScreenProps } from "@react-navigation/stack";
|
import useDark from "./use-dark";
|
||||||
|
|
||||||
const Drawer = createDrawerNavigator<DrawerParams>();
|
const Drawer = createDrawerNavigator<DrawerParams>();
|
||||||
|
|
||||||
interface AppDrawerParams {
|
interface AppDrawerParams {
|
||||||
settings: Settings;
|
startup: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AppDrawer({
|
export default function AppDrawer({
|
||||||
route,
|
route,
|
||||||
}: StackScreenProps<{ settings: AppDrawerParams }>) {
|
}: StackScreenProps<{ startup: AppDrawerParams }>) {
|
||||||
const dark = useDark();
|
const dark = useDark();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -31,9 +30,7 @@ export default function AppDrawer({
|
||||||
swipeEdgeWidth: 1000,
|
swipeEdgeWidth: 1000,
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
}}
|
}}
|
||||||
initialRouteName={
|
initialRouteName={(route.params.startup || "Home") as keyof DrawerParams}
|
||||||
(route.params.settings.startup || "Home") as keyof DrawerParams
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Drawer.Screen
|
<Drawer.Screen
|
||||||
name="Home"
|
name="Home"
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { Snackbar } from "react-native-paper";
|
||||||
|
import { emitter } from "./emitter";
|
||||||
|
import { TOAST } from "./toast";
|
||||||
|
|
||||||
|
export default function AppSnack({ textColor }: { textColor: string }) {
|
||||||
|
const [snackbar, setSnackbar] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const description = emitter.addListener(
|
||||||
|
TOAST,
|
||||||
|
({ value }: { value: string }) => {
|
||||||
|
setSnackbar(value);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return description.remove;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Snackbar
|
||||||
|
duration={3000}
|
||||||
|
onDismiss={() => setSnackbar("")}
|
||||||
|
visible={!!snackbar}
|
||||||
|
action={{
|
||||||
|
label: "Close",
|
||||||
|
onPress: () => setSnackbar(""),
|
||||||
|
textColor,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{snackbar}
|
||||||
|
</Snackbar>
|
||||||
|
);
|
||||||
|
}
|
|
@ -50,7 +50,7 @@ export type StackParams = {
|
||||||
|
|
||||||
const Stack = createStackNavigator<StackParams>();
|
const Stack = createStackNavigator<StackParams>();
|
||||||
|
|
||||||
export default function AppStack({ settings }: { settings: Settings }) {
|
export default function AppStack({ startup }: { startup: string }) {
|
||||||
return (
|
return (
|
||||||
<Stack.Navigator
|
<Stack.Navigator
|
||||||
screenOptions={{ headerShown: false, animationEnabled: false }}
|
screenOptions={{ headerShown: false, animationEnabled: false }}
|
||||||
|
@ -58,7 +58,7 @@ export default function AppStack({ settings }: { settings: Settings }) {
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="Drawer"
|
name="Drawer"
|
||||||
component={AppDrawer}
|
component={AppDrawer}
|
||||||
initialParams={{ settings }}
|
initialParams={{ startup }}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen name="EditSet" component={EditSet} />
|
<Stack.Screen name="EditSet" component={EditSet} />
|
||||||
<Stack.Screen name="EditSets" component={EditSets} />
|
<Stack.Screen name="EditSets" component={EditSets} />
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { emitter } from "./emitter";
|
||||||
|
import { TickEvent } from "./TimerPage";
|
||||||
|
import { ProgressBar } from "react-native-paper";
|
||||||
|
|
||||||
|
export default function TimerProgress() {
|
||||||
|
const [progress, setProgress] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const description = emitter.addListener(
|
||||||
|
"tick",
|
||||||
|
({ minutes, seconds }: TickEvent) => {
|
||||||
|
setProgress((Number(minutes) * 60 + Number(seconds)) / 210);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return description.remove;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (progress === 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProgressBar
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
bottom: 0,
|
||||||
|
}}
|
||||||
|
progress={progress}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
|
@ -85,8 +85,8 @@ android {
|
||||||
applicationId "com.massive"
|
applicationId "com.massive"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 36207
|
versionCode 36208
|
||||||
versionName "1.181"
|
versionName "1.182"
|
||||||
}
|
}
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
release {
|
release {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "massive",
|
"name": "massive",
|
||||||
"version": "1.181",
|
"version": "1.182",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
Loading…
Reference in New Issue