Skip to content

Commit 4e7045b

Browse files
committed
Lazy import Slate
1 parent 2ad1806 commit 4e7045b

File tree

2 files changed

+76
-55
lines changed

2 files changed

+76
-55
lines changed

src/hooks/useDeferredRender.ts

+22-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
import React, { useEffect, useState } from 'react';
1+
import React, { useEffect, useState, useRef } from 'react';
22
import 'requestidlecallback';
33

44
export interface UseDeferredRenderOptions {
55
timeout?: number;
66
}
77

88
export function useDeferredRender<T extends React.ReactNode>(
9-
expensiveRender: () => T,
9+
expensiveRender: () => T | Promise<T>,
1010
{ timeout }: UseDeferredRenderOptions = {},
1111
): T | null {
1212
const [initialized, setInitialized] = useState(false);
13+
const [content, setContent] = useState<T | null>(null);
14+
const isAsynRender = useRef(false);
1315
useEffect(() => {
1416
if (!initialized) {
1517
requestIdleCallback(() => {
@@ -18,5 +20,22 @@ export function useDeferredRender<T extends React.ReactNode>(
1820
}
1921
}, [initialized]);
2022

21-
return initialized ? expensiveRender() : null;
23+
if (isAsynRender.current) {
24+
isAsynRender.current = false;
25+
return content;
26+
}
27+
28+
if (initialized) {
29+
const renderResult = expensiveRender();
30+
if ('then' in renderResult) {
31+
renderResult.then(c => {
32+
isAsynRender.current = true;
33+
setContent(c);
34+
});
35+
} else {
36+
return renderResult;
37+
}
38+
}
39+
40+
return content;
2241
}

src/templates/CodePost.tsx

+54-52
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React, { HTMLAttributes, useEffect, useContext, useState, useMemo, useRef
22
import Layout from '../components/Layout';
33
import { graphql } from 'gatsby';
44
import RehypeReact from 'rehype-react';
5-
import { InteractiveCodeBlock } from '../components/InteractiveCodeBlock/InteractiveCodeBlock';
65
import { CacheableLineTokens, TypeScriptTokenType } from '../components/InteractiveCodeBlock/tokenizers/types';
76
import { useProgressiveTokenizer, ComposedTokenT } from '../hooks';
87
import { useDeferredRender } from '../hooks/useDeferredRender';
@@ -109,57 +108,60 @@ function ProgressiveCodeBlock(props: { children: [React.ReactElement<HTMLAttribu
109108
fileName,
110109
visibleSpan: span,
111110
});
112-
const deferredCodeBlock = useDeferredRender(() => (
113-
<InteractiveCodeBlock
114-
className="tm-theme"
115-
initialValue={originalText}
116-
tokenizer={tokenizer}
117-
readOnly={!isInitialized}
118-
onStartEditing={() => {
119-
setShouldInitialize(true);
120-
onStartEditing!(fileName);
121-
}}
122-
isLoading={isLoading}
123-
onChange={value => {
124-
const start = getStartOfCodeBlock(id, mutableCodeBlocks, sourceFiles![codeBlock.fileName]);
125-
const end = start + value.length;
126-
const newSpan = { start, length: value.length };
127-
mutableCodeBlocks[id].end = end;
128-
if (tsEnv && mutableCodeBlocks[id].text !== value) {
129-
mutableCodeBlocks[id].text = value;
130-
tsEnv.updateFile(fileName, value, span);
131-
}
132-
setSpan(newSpan);
133-
}}
134-
renderToken={(token, tokenProps) => {
135-
switch (token.type) {
136-
case 'tm':
137-
return (
138-
<span
139-
className={token.scopes.reduce((scopes, s) => `${scopes} ${s.split('.').join(' ')}`, '')}
140-
{...tokenProps}
141-
/>
142-
);
143-
case TypeScriptTokenType.Identifier:
144-
return (
145-
<TypeScriptIdentifierToken
146-
staticQuickInfo={quickInfo}
147-
languageService={tsEnv && isInitialized && tsEnv.languageService}
148-
sourceFileName={fileName}
149-
position={token.sourcePosition}
150-
{...tokenProps}
151-
/>
152-
);
153-
case TypeScriptTokenType.Diagnostic:
154-
return (
155-
<TypeScriptDiagnosticToken message={token.diagnosticMessage} {...tokenProps} />
156-
);
157-
default:
158-
return <span {...tokenProps} />;
159-
}
160-
}}
161-
/>
162-
), { timeout: 1000 });
111+
const deferredCodeBlock = useDeferredRender(async () => {
112+
const { InteractiveCodeBlock } = await import('../components/InteractiveCodeBlock/InteractiveCodeBlock');
113+
return (
114+
<InteractiveCodeBlock
115+
className="tm-theme"
116+
initialValue={originalText}
117+
tokenizer={tokenizer}
118+
readOnly={!isInitialized}
119+
onStartEditing={() => {
120+
setShouldInitialize(true);
121+
onStartEditing!(fileName);
122+
}}
123+
isLoading={isLoading}
124+
onChange={value => {
125+
const start = getStartOfCodeBlock(id, mutableCodeBlocks, sourceFiles![codeBlock.fileName]);
126+
const end = start + value.length;
127+
const newSpan = { start, length: value.length };
128+
mutableCodeBlocks[id].end = end;
129+
if (tsEnv && mutableCodeBlocks[id].text !== value) {
130+
mutableCodeBlocks[id].text = value;
131+
tsEnv.updateFile(fileName, value, span);
132+
}
133+
setSpan(newSpan);
134+
}}
135+
renderToken={(token, tokenProps) => {
136+
switch (token.type) {
137+
case 'tm':
138+
return (
139+
<span
140+
className={token.scopes.reduce((scopes, s) => `${scopes} ${s.split('.').join(' ')}`, '')}
141+
{...tokenProps}
142+
/>
143+
);
144+
case TypeScriptTokenType.Identifier:
145+
return (
146+
<TypeScriptIdentifierToken
147+
staticQuickInfo={quickInfo}
148+
languageService={tsEnv && isInitialized && tsEnv.languageService}
149+
sourceFileName={fileName}
150+
position={token.sourcePosition}
151+
{...tokenProps}
152+
/>
153+
);
154+
case TypeScriptTokenType.Diagnostic:
155+
return (
156+
<TypeScriptDiagnosticToken message={token.diagnosticMessage} {...tokenProps} />
157+
);
158+
default:
159+
return <span {...tokenProps} />;
160+
}
161+
}}
162+
/>
163+
);
164+
}, { timeout: 1000 });
163165

164166
return (
165167
<ErrorCatcher renderFallback={() => <CheapCodeBlock>{text}</CheapCodeBlock>}>

0 commit comments

Comments
 (0)