From 503254a58cfa36bfbc827369b20a82cb48900a1e Mon Sep 17 00:00:00 2001 From: rendi12345678 Date: Tue, 2 Jul 2024 03:07:40 +0700 Subject: [PATCH] Improve performance with lazy loading, useCallback, useMemo, and minified JavaScript --- package-lock.json | 2 +- package.json | 2 +- src/App.js | 27 ++++++++------ src/components/common/DynamicBackground.js | 2 +- src/components/common/FooterMenu.js | 2 +- src/components/common/Logo.js | 2 +- src/components/features/FreeTypingBox.js | 2 +- .../features/Keyboard/DefaultKeyboard.js | 2 +- .../features/SentenceBox/SentenceBox.js | 37 +++++++++++-------- .../features/SentenceBox/SentenceBoxStats.js | 2 +- src/components/features/TypeBox/Stats.js | 2 +- src/components/features/TypeBox/TypeBox.js | 2 +- .../features/WordsCard/WordsCard.js | 9 ++--- src/components/utils/Select.js | 2 +- src/scripts/wordsGenerator.js | 2 +- 15 files changed, 52 insertions(+), 45 deletions(-) diff --git a/package-lock.json b/package-lock.json index a0df879..22b0b94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "random-words": "^1.1.2", "react": "^18.0.0", "react-dom": "^18.0.0", - "react-scripts": "5.0.1", + "react-scripts": "^5.0.1", "react-select": "^5.3.0", "react-toastify": "^10.0.4", "styled-components": "^5.3.5", diff --git a/package.json b/package.json index 5676514..9bd499f 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "random-words": "^1.1.2", "react": "^18.0.0", "react-dom": "^18.0.0", - "react-scripts": "5.0.1", + "react-scripts": "^5.0.1", "react-select": "^5.3.0", "react-toastify": "^10.0.4", "styled-components": "^5.3.5", diff --git a/src/App.js b/src/App.js index d5c2f3f..9a01e91 100644 --- a/src/App.js +++ b/src/App.js @@ -1,28 +1,31 @@ -import React, { useState, useRef, useEffect } from "react"; +import React, { useState, Suspense, useRef, useEffect } from "react"; import { ThemeProvider } from "styled-components"; import { defaultTheme, themesOptions } from "./style/theme"; import { GlobalStyles } from "./style/global"; -import TypeBox from "./components/features/TypeBox/TypeBox"; -import SentenceBox from "./components/features/SentenceBox/SentenceBox"; -import Logo from "./components/common/Logo"; -import MusicPlayerSnackbar from "./components/features/MusicPlayer/MusicPlayerSnackbar"; -import FooterMenu from "./components/common/FooterMenu"; -import FreeTypingBox from "./components/features/FreeTypingBox"; + import { GAME_MODE, GAME_MODE_DEFAULT, GAME_MODE_SENTENCE, } from "./constants/Constants"; import useLocalPersistState from "./hooks/useLocalPersistState"; -import DefaultKeyboard from "./components/features/Keyboard/DefaultKeyboard"; -import WordsCard from "./components/features/WordsCard/WordsCard"; import { SOUND_MODE, soundOptions, DEFAULT_SOUND_TYPE, DEFAULT_SOUND_TYPE_KEY, } from "./components/features/sound/sound"; -import DynamicBackground from "./components/common/DynamicBackground"; + +// Lazy load components to boost performance +const TypeBox = React.lazy(() => import('./components/features/TypeBox/TypeBox')); +const SentenceBox = React.lazy(() => import('./components/features/SentenceBox/SentenceBox')); +const Logo = React.lazy(() => import('./components/common/Logo')); +const MusicPlayerSnackbar = React.lazy(() => import('./components/features/MusicPlayer/MusicPlayerSnackbar')); +const FooterMenu = React.lazy(() => import('./components/common/FooterMenu')); +const FreeTypingBox = React.lazy(() => import('./components/features/FreeTypingBox')); +const WordsCard = React.lazy(() => import('./components/features/WordsCard/WordsCard')); +const DefaultKeyboard = React.lazy(() => import('./components/features/Keyboard/DefaultKeyboard')); +const DynamicBackground = React.lazy(() => import('./components/common/DynamicBackground')); function App() { // localStorage persist theme setting @@ -174,7 +177,7 @@ function App() { return ( - <> +
@@ -245,7 +248,7 @@ function App() { onMouseLeave={() => focusTextInput()} >
- +
); } diff --git a/src/components/common/DynamicBackground.js b/src/components/common/DynamicBackground.js index fd78fcc..0faf48b 100644 --- a/src/components/common/DynamicBackground.js +++ b/src/components/common/DynamicBackground.js @@ -24,4 +24,4 @@ const DynamicBackground = ({ theme }) => { return null; }; -export default DynamicBackground; +export default React.memo(DynamicBackground); diff --git a/src/components/common/FooterMenu.js b/src/components/common/FooterMenu.js index 6ee1ad9..efd3ad9 100644 --- a/src/components/common/FooterMenu.js +++ b/src/components/common/FooterMenu.js @@ -253,4 +253,4 @@ const FooterMenu = ({ ); }; -export default FooterMenu; +export default React.memo(FooterMenu); diff --git a/src/components/common/Logo.js b/src/components/common/Logo.js index 45598f0..d423f12 100644 --- a/src/components/common/Logo.js +++ b/src/components/common/Logo.js @@ -15,4 +15,4 @@ const Logo = ({ isFocusedMode }) => { ); }; -export default Logo; +export default React.memo(Logo); diff --git a/src/components/features/FreeTypingBox.js b/src/components/features/FreeTypingBox.js index bfc87ac..cd010bb 100644 --- a/src/components/features/FreeTypingBox.js +++ b/src/components/features/FreeTypingBox.js @@ -51,4 +51,4 @@ const FreeTypingBox = ({ spaces = 4, textAreaRef, soundMode, soundType }) => { ); }; -export default FreeTypingBox; +export default React.memo(FreeTypingBox); diff --git a/src/components/features/Keyboard/DefaultKeyboard.js b/src/components/features/Keyboard/DefaultKeyboard.js index 63e83eb..8c40d19 100644 --- a/src/components/features/Keyboard/DefaultKeyboard.js +++ b/src/components/features/Keyboard/DefaultKeyboard.js @@ -222,4 +222,4 @@ const DefaultKeyboard = ({soundType, soundMode}) => { ); }; -export default DefaultKeyboard; +export default React.memo(DefaultKeyboard); diff --git a/src/components/features/SentenceBox/SentenceBox.js b/src/components/features/SentenceBox/SentenceBox.js index 7c7df70..34b80b3 100644 --- a/src/components/features/SentenceBox/SentenceBox.js +++ b/src/components/features/SentenceBox/SentenceBox.js @@ -1,5 +1,4 @@ -import React from "react"; -import { useState, useMemo, useEffect } from "react"; +import React, {useCallback, useMemo, useEffect, useState} from "react"; import { sentencesGenerator } from "../../../scripts/sentencesGenerator"; import { Stack } from "@mui/material"; import { Grid } from "@mui/material"; @@ -168,25 +167,31 @@ const SentenceBox = ({ extra: 0, }); - const checkAndUpdateStats = (currSentence, currInput) => { - const newStats = stats; +const checkAndUpdateStats = useCallback((currSentence, currInput) => { + setStats((prevStats) => { + let correct = prevStats.correct; + let incorrect = prevStats.incorrect; + let extra = prevStats.extra; + for (let i = 0; i < currSentence.length; i++) { if (currSentence[i] === currInput[i]) { - newStats.correct++; + correct++; } else { - newStats.incorrect++; + incorrect++; } } + const deltaCharDifference = currInput.length - currSentence.length; if (deltaCharDifference > 0) { - newStats.extra = deltaCharDifference; + extra = deltaCharDifference; } - setStats(newStats); - }; + return { correct, incorrect, extra }; + }); +}, []); - const handleKeyDown = (e) => { + const handleKeyDown = useCallback(() => (e) => { if (status !== "finished" && soundMode) { play(); } @@ -208,7 +213,7 @@ const SentenceBox = ({ return; } - setRawKeyStroke(rawKeyStroke + 1); + setRawKeyStroke(prevRawKeyStroke => prevRawKeyStroke + 1); // if enter key pressed. // advance to next sentence only if the input val length is equal to the current sentence char count); @@ -221,14 +226,14 @@ const SentenceBox = ({ setTimeRunning(false); return; } - setCurrSentenceIndex(currSentenceIndex + 1); + setCurrSentenceIndex(prevCurrSentenceIndex => prevCurrSentenceIndex + 1); setCurrInput(""); sentenceInputRef.current.value = ""; return; } return; } - }; + }); const getCharClassName = (idx, char) => { if (idx < currInput.length) { @@ -268,14 +273,14 @@ const SentenceBox = ({ } }; - const handleChange = (e) => { + const handleChange = useCallback(() => (e) => { const { currentTarget: { value }, } = e; if (e.currentTarget instanceof HTMLInputElement && !isOnComposition) { setCurrInput(value); } - }; + }); return (
@@ -452,4 +457,4 @@ const SentenceBox = ({ ); }; -export default SentenceBox; +export default React.memo(SentenceBox); diff --git a/src/components/features/SentenceBox/SentenceBoxStats.js b/src/components/features/SentenceBox/SentenceBoxStats.js index 58c6df8..b6e61fd 100644 --- a/src/components/features/SentenceBox/SentenceBoxStats.js +++ b/src/components/features/SentenceBox/SentenceBoxStats.js @@ -43,4 +43,4 @@ const SentenceBoxStats = ({ status, wpm, countDown, stats, rawKeyStrokes }) => { ); }; -export default SentenceBoxStats; +export default React.memo(SentenceBoxStats); diff --git a/src/components/features/TypeBox/Stats.js b/src/components/features/TypeBox/Stats.js index 10d1a81..3aee333 100644 --- a/src/components/features/TypeBox/Stats.js +++ b/src/components/features/TypeBox/Stats.js @@ -47,4 +47,4 @@ const Stats = ({ ); }; -export default Stats; +export default React.memo(Stats); diff --git a/src/components/features/TypeBox/TypeBox.js b/src/components/features/TypeBox/TypeBox.js index ac5963f..7e36416 100644 --- a/src/components/features/TypeBox/TypeBox.js +++ b/src/components/features/TypeBox/TypeBox.js @@ -991,4 +991,4 @@ const TypeBox = ({ ); }; -export default TypeBox; +export default React.memo(TypeBox); diff --git a/src/components/features/WordsCard/WordsCard.js b/src/components/features/WordsCard/WordsCard.js index f3ae6c1..b2691fd 100644 --- a/src/components/features/WordsCard/WordsCard.js +++ b/src/components/features/WordsCard/WordsCard.js @@ -1,5 +1,4 @@ -import React from "react"; -import { useState, useEffect, useRef } from "react"; +import React, { useState, useCallback, useEffect, useRef } from "react"; import { DICTIONARY_SOURCE_CATALOG } from "../../../constants/DictionaryConstants"; import { RECITE_MODE_TITLE, @@ -100,11 +99,11 @@ const WordsCard = ({ soundType, soundMode }) => { } }; - const handleInputChange = (e) => { + const handleInputChange = useCallback(() => (e) => { setCurrInput(e.target.value); hiddenInputRef.current.value = e.target.value; e.preventDefault(); - }; + }); const updateAlphabetSet = (char) => { const newAlphabetSet = new Set(alphabetSet); @@ -516,4 +515,4 @@ const WordsCard = ({ soundType, soundMode }) => { ); }; -export default WordsCard; +export default React.memo(WordsCard); diff --git a/src/components/utils/Select.js b/src/components/utils/Select.js index 50be847..f84daa0 100644 --- a/src/components/utils/Select.js +++ b/src/components/utils/Select.js @@ -54,4 +54,4 @@ export default styled(Select)` background: ${({ theme }) => theme.background}; } -`; \ No newline at end of file +`; diff --git a/src/scripts/wordsGenerator.js b/src/scripts/wordsGenerator.js index f9d8ff2..8fdf336 100644 --- a/src/scripts/wordsGenerator.js +++ b/src/scripts/wordsGenerator.js @@ -139,6 +139,6 @@ const wordsCardVocabGenerator = (vocabSource, chapter) => { wordsList.push(VOCAB_DICTIONARIES[vocabSource][i]); } return wordsList; -}; +} export { wordsGenerator, chineseWordsGenerator, wordsCardVocabGenerator };