Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit81e7da3

Browse files
authored
Denote suspenseful components with comment markers (#376)
* Denote suspenseful components with comment markers* Add changeset* use shorter notation
1 parentb2050f4 commit81e7da3

File tree

3 files changed

+174
-21
lines changed

3 files changed

+174
-21
lines changed

‎.changeset/happy-peas-type.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'preact-render-to-string':minor
3+
---
4+
5+
Insert comment markers for suspended trees, only in renderToStringAsync

‎src/index.js‎

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ const EMPTY_ARR = [];
2929
constisArray=Array.isArray;
3030
constassign=Object.assign;
3131
constEMPTY_STR='';
32+
constBEGIN_SUSPENSE_DENOMINATOR='<!--$s-->';
33+
constEND_SUSPENSE_DENOMINATOR='<!--/$s-->';
3234

3335
// Global state for the current render pass
3436
letbeforeDiff,afterDiff,renderHook,ummountHook;
@@ -372,7 +374,14 @@ function _renderToString(
372374

373375
if(renderHook)renderHook(vnode);
374376

375-
rendered=type.call(component,props,cctx);
377+
try{
378+
rendered=type.call(component,props,cctx);
379+
}catch(e){
380+
if(asyncMode){
381+
vnode._suspended=true;
382+
}
383+
throwe;
384+
}
376385
}
377386
component[DIRTY]=true;
378387
}
@@ -403,6 +412,7 @@ function _renderToString(
403412
selectValue,
404413
vnode,
405414
asyncMode,
415+
false,
406416
renderer
407417
);
408418
}catch(err){
@@ -475,6 +485,21 @@ function _renderToString(
475485

476486
if(options.unmount)options.unmount(vnode);
477487

488+
if(vnode._suspended){
489+
if(typeofstr==='string'){
490+
returnBEGIN_SUSPENSE_DENOMINATOR+str+END_SUSPENSE_DENOMINATOR;
491+
}elseif(isArray(str)){
492+
str.unshift(BEGIN_SUSPENSE_DENOMINATOR);
493+
str.push(END_SUSPENSE_DENOMINATOR);
494+
returnstr;
495+
}
496+
497+
returnstr.then(
498+
(resolved)=>
499+
BEGIN_SUSPENSE_DENOMINATOR+resolved+END_SUSPENSE_DENOMINATOR
500+
);
501+
}
502+
478503
returnstr;
479504
}catch(error){
480505
if(!asyncMode&&renderer&&renderer.onError){
@@ -503,7 +528,7 @@ function _renderToString(
503528

504529
constrenderNestedChildren=()=>{
505530
try{
506-
return_renderToString(
531+
constresult=_renderToString(
507532
rendered,
508533
context,
509534
isSvgMode,
@@ -512,22 +537,26 @@ function _renderToString(
512537
asyncMode,
513538
renderer
514539
);
540+
returnvnode._suspended
541+
?BEGIN_SUSPENSE_DENOMINATOR+result+END_SUSPENSE_DENOMINATOR
542+
:result;
515543
}catch(e){
516544
if(!e||typeofe.then!='function')throwe;
517545

518-
returne.then(
519-
()=>
520-
_renderToString(
521-
rendered,
522-
context,
523-
isSvgMode,
524-
selectValue,
525-
vnode,
526-
asyncMode,
527-
renderer
528-
),
529-
renderNestedChildren
530-
);
546+
returne.then(()=>{
547+
constresult=_renderToString(
548+
rendered,
549+
context,
550+
isSvgMode,
551+
selectValue,
552+
vnode,
553+
asyncMode,
554+
renderer
555+
);
556+
returnvnode._suspended
557+
?BEGIN_SUSPENSE_DENOMINATOR+result+END_SUSPENSE_DENOMINATOR
558+
:result;
559+
},renderNestedChildren);
531560
}
532561
};
533562

‎test/compat/async.test.jsx‎

Lines changed: 125 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { h, Fragment } from 'preact';
33
import{Suspense,useId,lazy,createContext}from'preact/compat';
44
import{expect}from'chai';
55
import{createSuspender}from'../utils.jsx';
6+
constwait=(ms)=>newPromise((r)=>setTimeout(r,ms));
67

78
describe('Async renderToString',()=>{
89
it('should render JSX after a suspense boundary',async()=>{
@@ -16,7 +17,30 @@ describe('Async renderToString', () => {
1617
</Suspense>
1718
);
1819

19-
constexpected=`<div class="foo">bar</div>`;
20+
constexpected=`<!--$s--><div class="foo">bar</div><!--/$s-->`;
21+
22+
suspended.resolve();
23+
24+
constrendered=awaitpromise;
25+
26+
expect(rendered).to.equal(expected);
27+
});
28+
29+
it('should correctly denote null returns of suspending components',async()=>{
30+
const{ Suspender, suspended}=createSuspender();
31+
32+
constAnalytics=()=>null;
33+
34+
constpromise=renderToStringAsync(
35+
<Suspensefallback={<div>loading...</div>}>
36+
<Suspender>
37+
<Analytics/>
38+
</Suspender>
39+
<divclass="foo">bar</div>
40+
</Suspense>
41+
);
42+
43+
constexpected=`<!--$s--><!--/$s--><div class="foo">bar</div>`;
2044

2145
suspended.resolve();
2246

@@ -49,7 +73,7 @@ describe('Async renderToString', () => {
4973
</ul>
5074
);
5175

52-
constexpected=`<ul><li>one</li><li>two</li><li>three</li></ul>`;
76+
constexpected=`<ul><!--$s--><li>one</li><!--$s--><li>two</li><!--/$s--><li>three</li><!--/$s--></ul>`;
5377

5478
suspendedOne.resolve();
5579
suspendedTwo.resolve();
@@ -85,10 +109,102 @@ describe('Async renderToString', () => {
85109
</ul>
86110
);
87111

88-
constexpected=`<ul><li>one</li><li>two</li><li>three</li></ul>`;
112+
constexpected=`<ul><!--$s--><li>one</li><!--$s--><li>two</li><!--/$s--><li>three</li><!--/$s--></ul>`;
113+
114+
suspendedOne.resolve();
115+
suspendedTwo.resolve();
116+
117+
constrendered=awaitpromise;
118+
119+
expect(rendered).to.equal(expected);
120+
});
121+
122+
it('should render JSX with nested suspense boundaries containing multiple suspending components',async()=>{
123+
const{
124+
Suspender:SuspenderOne,
125+
suspended:suspendedOne
126+
}=createSuspender();
127+
const{
128+
Suspender:SuspenderTwo,
129+
suspended:suspendedTwo
130+
}=createSuspender();
131+
const{
132+
Suspender:SuspenderThree,
133+
suspended:suspendedThree
134+
}=createSuspender('three');
135+
136+
constpromise=renderToStringAsync(
137+
<ul>
138+
<Suspensefallback={null}>
139+
<SuspenderOne>
140+
<li>one</li>
141+
<Suspensefallback={null}>
142+
<SuspenderTwo>
143+
<li>two</li>
144+
</SuspenderTwo>
145+
<SuspenderThree>
146+
<li>three</li>
147+
</SuspenderThree>
148+
</Suspense>
149+
<li>four</li>
150+
</SuspenderOne>
151+
</Suspense>
152+
</ul>
153+
);
154+
155+
constexpected=`<ul><!--$s--><li>one</li><!--$s--><li>two</li><!--/$s--><!--$s--><li>three</li><!--/$s--><li>four</li><!--/$s--></ul>`;
89156

90157
suspendedOne.resolve();
91158
suspendedTwo.resolve();
159+
awaitwait(0);
160+
suspendedThree.resolve();
161+
162+
constrendered=awaitpromise;
163+
164+
expect(rendered).to.equal(expected);
165+
});
166+
167+
it('should render JSX with deeply nested suspense boundaries',async()=>{
168+
const{
169+
Suspender:SuspenderOne,
170+
suspended:suspendedOne
171+
}=createSuspender();
172+
const{
173+
Suspender:SuspenderTwo,
174+
suspended:suspendedTwo
175+
}=createSuspender();
176+
const{
177+
Suspender:SuspenderThree,
178+
suspended:suspendedThree
179+
}=createSuspender();
180+
181+
constpromise=renderToStringAsync(
182+
<ul>
183+
<Suspensefallback={null}>
184+
<SuspenderOne>
185+
<li>one</li>
186+
<Suspensefallback={null}>
187+
<SuspenderTwo>
188+
<li>two</li>
189+
<Suspensefallback={null}>
190+
<SuspenderThree>
191+
<li>three</li>
192+
</SuspenderThree>
193+
</Suspense>
194+
</SuspenderTwo>
195+
</Suspense>
196+
<li>four</li>
197+
</SuspenderOne>
198+
</Suspense>
199+
</ul>
200+
);
201+
202+
constexpected=`<ul><!--$s--><li>one</li><!--$s--><li>two</li><!--$s--><li>three</li><!--/$s--><!--/$s--><li>four</li><!--/$s--></ul>`;
203+
204+
suspendedOne.resolve();
205+
suspendedTwo.resolve();
206+
awaitwait(0);
207+
suspendedThree.resolve();
92208

93209
constrendered=awaitpromise;
94210

@@ -127,7 +243,7 @@ describe('Async renderToString', () => {
127243
</ul>
128244
);
129245

130-
constexpected=`<ul><li>one</li><li>two</li><li>three</li></ul>`;
246+
constexpected=`<ul><!--$s--><li>one</li><!--/$s--><!--$s--><li>two</li><!--/$s--><!--$s--><li>three</li><!--/$s--></ul>`;
131247

132248
suspendedOne.resolve();
133249
suspendedTwo.resolve();
@@ -187,7 +303,7 @@ describe('Async renderToString', () => {
187303

188304
suspended.resolve();
189305
constrendered=awaitpromise;
190-
expect(rendered).to.equal('<p>ok</p>');
306+
expect(rendered).to.equal('<!--$s--><p>ok</p><!--/$s-->');
191307
});
192308

193309
it('should work with an in-render suspension',async()=>{
@@ -224,7 +340,10 @@ describe('Async renderToString', () => {
224340
</Context.Provider>
225341
);
226342

227-
expect(rendered).to.equal(`<div>2</div>`);
343+
// Before we get to the actual DOM this suspends twice
344+
expect(rendered).to.equal(
345+
`<!--$s--><!--$s--><div>2</div><!--/$s--><!--/$s-->`
346+
);
228347
});
229348

230349
describe('dangerouslySetInnerHTML',()=>{

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp