kamilmielnik / scrabble-solver

Free, open-source, cross-platform, and multi-language analysis tool for Scrabble, Super Scrabble & Literaki. Quickly find top scoring words using given board & tiles. Available in English, French, German, Persian, Polish, Romanian, Spanish & Turkish.
https://scrabble-solver.org
Other
94 stars 20 forks source link

Initial rendering performance #264

Closed kamilmielnik closed 1 year ago

kamilmielnik commented 1 year ago

This is what currently affects the lighthouse score the most. Ideally the splash screen is not required anymore.

kamilmielnik commented 1 year ago

Also:

useMedia When server side rendering, defaultState should be defined to prevent a hydration mismatches.

kamilmielnik commented 1 year ago

react-use's useMeasure causes rerenders as the size object reference can change even though the size did not.

The hook could be wrapped and memoized with a custom equality comparison, but I'm a bit tired with react-use now. It's too big, and I don't use that much of it. I'll remove it. This is the solution: https://github.com/pmndrs/react-use-measure

kamilmielnik commented 1 year ago

Why did you render

npm install @welldone-software/why-did-you-render --save-dev
diff --git a/packages/scrabble-solver/next.config.js b/packages/scrabble-solver/next.config.js
index 08b88d5f..4ae92a27 100644
--- a/packages/scrabble-solver/next.config.js
+++ b/packages/scrabble-solver/next.config.js
@@ -20,35 +20,58 @@ module.exports = {
     buildActivity: false,
   },
   reactStrictMode: true,
-  webpack: (config) => ({
-    ...config,
-    resolve: {
-      ...config.resolve,
-      alias: {
-        ...config.resolve.alias,
-        ...tsConfigAliases,
-      },
-    },
-    module: {
-      ...config.module,
-      rules: [
-        ...config.module.rules,
-        {
-          test: /\.svg$/,
-          issuer: /\.tsx?$/,
-          use: ['@svgr/webpack'],
+  webpack: (config, { dev, isServer }) => {
+    if (dev && !isServer) {
+      const originalEntry = config.entry;
+      config.entry = async () => {
+        const wdrPath = path.resolve(__dirname, './src/wdyr.ts');
+        const entries = await originalEntry();
+
+        if (entries['main.js'] && !entries['main.js'].includes(wdrPath)) {
+          entries['main.js'].push(wdrPath);
+        }
+        return entries;
+      };
+    }
+
+    return {
+      ...config,
+      resolve: {
+        ...config.resolve,
+        alias: {
+          ...config.resolve.alias,
+          ...tsConfigAliases,
         },
-      ],
-    },
-    plugins: [
-      ...config.plugins,
-      process.env.NODE_ENV === 'production'
-        ? new WorkboxPlugin.InjectManifest({
-            swSrc: path.join(__dirname, 'src/service-worker/index.ts'),
-            swDest: path.join(__dirname, 'public/service-worker.js'),
-            exclude: [/\.map$/, /\.next/, /_next/, /manifest/, /\.htaccess$/, /.*\/static\/.*/, /service-worker\.js$/],
-          })
-        : undefined,
-    ].filter(Boolean),
-  }),
+      },
+      module: {
+        ...config.module,
+        rules: [
+          ...config.module.rules,
+          {
+            test: /\.svg$/,
+            issuer: /\.tsx?$/,
+            use: ['@svgr/webpack'],
+          },
+        ],
+      },
+      plugins: [
+        ...config.plugins,
+        process.env.NODE_ENV === 'production'
+          ? new WorkboxPlugin.InjectManifest({
+              swSrc: path.join(__dirname, 'src/service-worker/index.ts'),
+              swDest: path.join(__dirname, 'public/service-worker.js'),
+              exclude: [
+                /\.map$/,
+                /\.next/,
+                /_next/,
+                /manifest/,
+                /\.htaccess$/,
+                /.*\/static\/.*/,
+                /service-worker\.js$/,
+              ],
+            })
+          : undefined,
+      ].filter(Boolean),
+    };
+  },
 };
diff --git a/packages/scrabble-solver/src/pages/_app.tsx b/packages/scrabble-solver/src/pages/_app.tsx
index 4c77a772..a1218ece 100644
--- a/packages/scrabble-solver/src/pages/_app.tsx
+++ b/packages/scrabble-solver/src/pages/_app.tsx
@@ -3,6 +3,8 @@ import Head from 'next/head';
 import { FunctionComponent } from 'react';
 import { Provider } from 'react-redux';

+import '../wdyr';
+
 import { SeoMessage } from 'components';
 import { createAppStore } from 'state';

diff --git a/packages/scrabble-solver/src/pages/index.tsx b/packages/scrabble-solver/src/pages/index.tsx
index a439bb8c..8c8e0615 100644
--- a/packages/scrabble-solver/src/pages/index.tsx
+++ b/packages/scrabble-solver/src/pages/index.tsx
@@ -4,7 +4,8 @@ import path from 'path';
 import { FunctionComponent, useEffect, useState } from 'react';
 import ReactModal from 'react-modal';
 import { useDispatch } from 'react-redux';
-import { useEffectOnce, useMeasure } from 'react-use';
+import { useEffectOnce } from 'react-use';
+import useMeasure from 'react-use-measure';

 import { Logo, LogoSplashScreen, NavButtons, Solver, SvgFontFix } from 'components';
 import { useAppLayout, useDirection, useLanguage, useLocalStorage } from 'hooks';
@@ -42,12 +43,14 @@ const Index: FunctionComponent<Props> = ({ version }) => {
   const [showSettings, setShowSettings] = useState(false);
   const [showWords, setShowWords] = useState(false);
   const [isInitialized, setIsInitialized] = useState(false);
-  const [indexRef, { height: indexHeight, width: indexWidth }] = useMeasure<HTMLDivElement>();
-  const [navRef, { height: navHeight }] = useMeasure<HTMLElement>();
+  const [indexRef, { height: indexHeight, width: indexWidth }] = useMeasure();
+  const [navRef, { height: navHeight }] = useMeasure();
   const solverHeight = indexHeight - navHeight;
   const solverWidth = indexWidth;
   const [isClient, setIsClient] = useState(false);

+  console.log('render Index');
+
   const handleClear = () => {
     dispatch(reset());
   };
@@ -151,3 +154,5 @@ const readVersion = async (): Promise<string> => {
 };

 export default Index;
+
+Index.whyDidYouRender = true;
diff --git a/packages/scrabble-solver/tsconfig.json b/packages/scrabble-solver/tsconfig.json
index 0455aaf2..087a514b 100644
--- a/packages/scrabble-solver/tsconfig.json
+++ b/packages/scrabble-solver/tsconfig.json
@@ -28,7 +28,8 @@
     "rootDir": "./src",
     "skipLibCheck": true,
     "typeRoots": ["./node_modules/@types", "./src/@types"],
-    "incremental": true
+    "incremental": true,
+    "jsxImportSource": "@welldone-software/why-did-you-render"
   },
   "exclude": ["node_modules"],
   "files": ["./src/@types/scss.d.ts", "./src/@types/svg.d.ts"],
kamilmielnik commented 1 year ago

TODO:

kamilmielnik commented 1 year ago

2 renders are left. The second one is because of isClient - it's needed as there is no viewport size information on the server, so SSR is pointless as it's never going to match anyway.