Просмотр исходного кода

Merge pull request #7559 from weseek/support/use-react-markdown-customkeyprop

support: Use react-markdown-customkeyprop to reduce re-rendering
Yuki Takei 3 лет назад
Родитель
Сommit
40c806120a

+ 1 - 1
packages/app/next.config.js

@@ -23,7 +23,7 @@ const getTranspilePackages = () => {
   const packages = [
     ...listScopedPackages(['@growi'], { ignorePackageNames: ['@growi/app'] }),
     // listing ESM packages until experimental.esmExternals works correctly to avoid ERR_REQUIRE_ESM
-    'react-markdown',
+    'react-markdown-customkeyprop',
     'unified',
     'markdown-table',
     'character-entities-html4',

+ 1 - 1
packages/app/package.json

@@ -163,7 +163,7 @@
     "react-error-boundary": "^3.1.4",
     "react-i18next": "^12.2.0",
     "react-image-crop": "^8.3.0",
-    "react-markdown": "^8.0.3",
+    "react-markdown-customkeyprop": "^8.0.6-customkeyprop.0",
     "react-multiline-clamp": "^2.0.0",
     "react-scroll": "^1.8.7",
     "react-syntax-highlighter": "^15.5.0",

+ 1 - 1
packages/app/src/components/Page/RevisionRenderer.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 
 import { ErrorBoundary, FallbackProps } from 'react-error-boundary';
-import ReactMarkdown from 'react-markdown';
+import ReactMarkdown from 'react-markdown-customkeyprop';
 
 import type { RendererOptions } from '~/services/renderer/renderer';
 import loggerFactory from '~/utils/logger';

+ 1 - 1
packages/app/src/components/PagePresentationModal.tsx

@@ -3,7 +3,7 @@ import React, { useCallback } from 'react';
 import type { PresentationProps } from '@growi/presentation';
 import { useFullScreen } from '@growi/ui';
 import dynamic from 'next/dynamic';
-import type { ReactMarkdownOptions } from 'react-markdown/lib/react-markdown';
+import type { ReactMarkdownOptions } from 'react-markdown-customkeyprop/lib/react-markdown';
 import {
   Modal, ModalBody,
 } from 'reactstrap';

+ 1 - 1
packages/app/src/components/ReactMarkdownComponents/CodeBlock.tsx

@@ -1,6 +1,6 @@
 import { ReactNode } from 'react';
 
-import type { CodeComponent } from 'react-markdown/lib/ast-to-react';
+import type { CodeComponent } from 'react-markdown-customkeyprop/lib/ast-to-react';
 import { PrismAsyncLight } from 'react-syntax-highlighter';
 import { oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
 

+ 1 - 1
packages/app/src/components/ReactMarkdownComponents/Header.tsx

@@ -3,7 +3,7 @@ import { useCallback, useEffect, useState } from 'react';
 import EventEmitter from 'events';
 
 import { useRouter } from 'next/router';
-import { Element } from 'react-markdown/lib/rehype-filter';
+import { Element } from 'react-markdown-customkeyprop/lib/rehype-filter';
 
 import { useIsGuestUser, useIsSharedUser, useShareLinkId } from '~/stores/context';
 import loggerFactory from '~/utils/logger';

+ 1 - 1
packages/app/src/components/ReactMarkdownComponents/TableWithEditButton.tsx

@@ -2,7 +2,7 @@ import React, { useCallback } from 'react';
 
 import EventEmitter from 'events';
 
-import { Element } from 'react-markdown/lib/rehype-filter';
+import { Element } from 'react-markdown-customkeyprop/lib/rehype-filter';
 
 import { useIsGuestUser, useIsSharedUser, useShareLinkId } from '~/stores/context';
 

+ 1 - 1
packages/app/src/components/TableOfContents.tsx

@@ -1,7 +1,7 @@
 import React, { useCallback } from 'react';
 
 import { pagePathUtils } from '@growi/core';
-import ReactMarkdown from 'react-markdown';
+import ReactMarkdown from 'react-markdown-customkeyprop';
 
 import { useCurrentPagePath } from '~/stores/page';
 import { useTocOptions } from '~/stores/renderer';

+ 0 - 20
packages/app/src/services/renderer/remark-plugins/table.ts

@@ -1,20 +0,0 @@
-import { Plugin } from 'unified';
-import { visit } from 'unist-util-visit';
-
-export const remarkPlugin: Plugin = function() {
-  return (tree) => {
-    visit(tree, (node) => {
-      if (node.type === 'table' || node.type === 'tableCell' || node.type === 'tableRow') {
-
-        // omit position to fix the key regardless of its position
-        // see:
-        //   https://github.com/remarkjs/react-markdown/issues/703
-        //   https://github.com/remarkjs/react-markdown/issues/466
-        //
-        //   https://github.com/remarkjs/react-markdown/blob/a80dfdee2703d84ac2120d28b0e4998a5b417c85/lib/ast-to-react.js#L201-L204
-        //   https://github.com/remarkjs/react-markdown/blob/a80dfdee2703d84ac2120d28b0e4998a5b417c85/lib/ast-to-react.js#L217-L222
-        delete node.position;
-      }
-    });
-  };
-};

+ 4 - 7
packages/app/src/services/renderer/renderer.tsx

@@ -7,9 +7,9 @@ import growiDirective from '@growi/remark-growi-directive';
 import { Lsx, LsxImmutable } from '@growi/remark-lsx/components';
 import * as lsxGrowiPlugin from '@growi/remark-lsx/services/renderer';
 import type { Schema as SanitizeOption } from 'hast-util-sanitize';
-import type { SpecialComponents } from 'react-markdown/lib/ast-to-react';
-import type { NormalComponents } from 'react-markdown/lib/complex-types';
-import type { ReactMarkdownOptions } from 'react-markdown/lib/react-markdown';
+import type { SpecialComponents } from 'react-markdown-customkeyprop/lib/ast-to-react';
+import type { NormalComponents } from 'react-markdown-customkeyprop/lib/complex-types';
+import type { ReactMarkdownOptions } from 'react-markdown-customkeyprop/lib/react-markdown';
 import katex from 'rehype-katex';
 import raw from 'rehype-raw';
 import sanitize, { defaultSchema as rehypeSanitizeDefaultSchema } from 'rehype-sanitize';
@@ -72,7 +72,7 @@ const baseSanitizeSchema = {
     iframe: ['allow', 'referrerpolicy', 'sandbox', 'src', 'srcdoc'],
     // The special value 'data*' as a property name can be used to allow all data properties.
     // see: https://github.com/syntax-tree/hast-util-sanitize/
-    '*': ['class', 'className', 'style', 'data*'],
+    '*': ['key', 'class', 'className', 'style', 'data*'],
   },
 };
 
@@ -261,7 +261,6 @@ export const generateSimpleViewOptions = (
     drawioPlugin.remarkPlugin,
     xsvToTable.remarkPlugin,
     lsxGrowiPlugin.remarkPlugin,
-    // table.remarkPlugin,
   );
 
   const isEnabledLinebreaks = overrideIsEnabledLinebreaks ?? config.isEnabledLinebreaks;
@@ -329,7 +328,6 @@ export const generateSSRViewOptions = (
     math,
     xsvToTable.remarkPlugin,
     lsxGrowiPlugin.remarkPlugin,
-    // table.remarkPlugin,
   );
 
   const isEnabledLinebreaks = config.isEnabledLinebreaks;
@@ -380,7 +378,6 @@ export const generatePreviewOptions = (config: RendererConfig, pagePath: string)
     drawioPlugin.remarkPlugin,
     xsvToTable.remarkPlugin,
     lsxGrowiPlugin.remarkPlugin,
-    // table.remarkPlugin,
   );
   if (config.isEnabledLinebreaks) {
     remarkPlugins.push(breaks);

+ 1 - 1
packages/presentation/package.json

@@ -23,7 +23,7 @@
     "eslint-plugin-regex": "^1.8.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
-    "react-markdown": "^8.0.3",
+    "react-markdown-customkeyprop": "^8.0.6-customkeyprop.0",
     "reveal.js": "^4.4.0"
   }
 }

+ 1 - 1
packages/presentation/src/components/Slides.tsx

@@ -3,7 +3,7 @@ import React from 'react';
 import { Marp } from '@marp-team/marp-core';
 import { Element } from '@marp-team/marpit';
 import Head from 'next/head';
-import { ReactMarkdown } from 'react-markdown/lib/react-markdown';
+import { ReactMarkdown } from 'react-markdown-customkeyprop/lib/react-markdown';
 
 import type { PresentationOptions } from '../consts';
 import * as extractSections from '../services/renderer/extract-sections';

+ 1 - 1
packages/presentation/src/consts/index.ts

@@ -1,4 +1,4 @@
-import type { ReactMarkdownOptions } from 'react-markdown/lib/react-markdown';
+import type { ReactMarkdownOptions } from 'react-markdown-customkeyprop/lib/react-markdown';
 import type { Options as RevealOptions } from 'reveal.js';
 
 export type PresentationOptions = {

+ 1 - 9
packages/remark-drawio/src/services/renderer/remark-drawio.ts

@@ -21,6 +21,7 @@ function rewriteNode(node: Node, index: number) {
     diagramIndex: index,
     bol: node.position?.start.line,
     eol: node.position?.end.line,
+    key: `drawio-${index}`,
   };
 }
 
@@ -30,15 +31,6 @@ export const remarkPlugin: Plugin = function() {
       if (node.type === 'code') {
         if (isDrawioBlock(node.lang)) {
           rewriteNode(node, index ?? 0);
-
-          // omit position to fix the key regardless of its position
-          // see:
-          //   https://github.com/remarkjs/react-markdown/issues/703
-          //   https://github.com/remarkjs/react-markdown/issues/466
-          //
-          //   https://github.com/remarkjs/react-markdown/blob/a80dfdee2703d84ac2120d28b0e4998a5b417c85/lib/ast-to-react.js#L201-L204
-          //   https://github.com/remarkjs/react-markdown/blob/a80dfdee2703d84ac2120d28b0e4998a5b417c85/lib/ast-to-react.js#L217-L222
-          delete node.position;
         }
       }
     });

+ 3 - 11
packages/remark-lsx/src/services/renderer/lsx.ts

@@ -11,14 +11,14 @@ import { visit } from 'unist-util-visit';
 const NODE_NAME_PATTERN = new RegExp(/ls|lsx/);
 const SUPPORTED_ATTRIBUTES = ['prefix', 'num', 'depth', 'sort', 'reverse', 'filter', 'except', 'isSharedPage'];
 
-const { addHeadingSlash, hasHeadingSlash } = pathUtils;
+const { hasHeadingSlash } = pathUtils;
 
 type DirectiveAttributes = Record<string, string>
 
 
 export const remarkPlugin: Plugin = function() {
   return (tree) => {
-    visit(tree, (node) => {
+    visit(tree, (node, index) => {
       if (node.type === remarkGrowiDirectivePluginType.Text || node.type === remarkGrowiDirectivePluginType.Leaf) {
         if (typeof node.name !== 'string') {
           return;
@@ -46,18 +46,10 @@ export const remarkPlugin: Plugin = function() {
             }
           }
         }
+        attributes.key = `lsx-${index}`;
 
         data.hName = 'lsx';
         data.hProperties = attributes;
-
-        // omit position to fix the key regardless of its position
-        // see:
-        //   https://github.com/remarkjs/react-markdown/issues/703
-        //   https://github.com/remarkjs/react-markdown/issues/466
-        //
-        //   https://github.com/remarkjs/react-markdown/blob/a80dfdee2703d84ac2120d28b0e4998a5b417c85/lib/ast-to-react.js#L201-L204
-        //   https://github.com/remarkjs/react-markdown/blob/a80dfdee2703d84ac2120d28b0e4998a5b417c85/lib/ast-to-react.js#L217-L222
-        delete node.position;
       }
     });
   };

+ 4 - 4
yarn.lock

@@ -17881,10 +17881,10 @@ react-lifecycles-compat@^3.0.4:
   resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
   integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
 
-react-markdown@^8.0.3:
-  version "8.0.5"
-  resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-8.0.5.tgz#c9a70a33ca9aeeafb769c6582e7e38843b9d70ad"
-  integrity sha512-jGJolWWmOWAvzf+xMdB9zwStViODyyFQhNB/bwCerbBKmrTmgmA599CGiOlP58OId1IMoIRsA8UdI1Lod4zb5A==
+react-markdown-customkeyprop@^8.0.6-customkeyprop.0:
+  version "8.0.6-customkeyprop.0"
+  resolved "https://registry.yarnpkg.com/react-markdown-customkeyprop/-/react-markdown-customkeyprop-8.0.6-customkeyprop.0.tgz#050a05dc204745f063ac44b1174b834468cc0112"
+  integrity sha512-9L03/4rhGM9K78c9FT1AXeyk0R5esghGmVOxXcpoaZeSI+zMLJ6spP5B2URHPqVahPk+YTgQso/Ba53a6R+oaQ==
   dependencies:
     "@types/hast" "^2.0.0"
     "@types/prop-types" "^15.0.0"