Fixes hydration of Maps and Sets (#3960)

This commit is contained in:
Matthew Phillips 2022-07-18 16:43:09 -04:00 committed by GitHub
parent 5fde2fd8bc
commit ceda294e13
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 50 additions and 5 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Fixes hydration of maps/sets

View file

@ -4,12 +4,15 @@ import { useState } from 'react';
interface Props { interface Props {
obj: BigNestedObject; obj: BigNestedObject;
num: bigint; num: bigint;
arr: any[];
map: Map<string, string>;
set: Set<string>;
} }
const isNode = typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]'; const isNode = typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]';
/** a counter written in React */ /** a counter written in React */
export default function Component({ obj, num, arr }: Props) { export default function Component({ obj, num, arr, map, set }: Props) {
// We are testing hydration, so don't return anything in the server. // We are testing hydration, so don't return anything in the server.
if(isNode) { if(isNode) {
return <div></div> return <div></div>
@ -24,6 +27,13 @@ export default function Component({ obj, num, arr }: Props) {
<span id="bigint-value">{num.toString()}</span> <span id="bigint-value">{num.toString()}</span>
<span id="arr-type">{Object.prototype.toString.call(arr)}</span> <span id="arr-type">{Object.prototype.toString.call(arr)}</span>
<span id="arr-value">{arr.join(',')}</span> <span id="arr-value">{arr.join(',')}</span>
<span id="map-type">{Object.prototype.toString.call(map)}</span>
<ul id="map-items">{Array.from(map).map(([key, value]) => (
<li>{key}: {value}</li>
))}
</ul>
<span id="set-type">{Object.prototype.toString.call(set)}</span>
<span id="set-value">{Array.from(set).join(',')}</span>
</div> </div>
); );
} }

View file

@ -12,6 +12,14 @@ const obj: BigNestedObject = {
} }
} }
}; };
const map = new Map<string,string>();
map.set('test1', 'test2');
map.set('test3', 'test4');
const set = new Set<string>();
set.add('test1');
set.add('test2');
--- ---
<html lang="en"> <html lang="en">
@ -22,7 +30,7 @@ const obj: BigNestedObject = {
</head> </head>
<body> <body>
<main> <main>
<Component client:load obj={obj} num={11n} arr={[0, "foo"]} /> <Component client:load obj={obj} num={11n} arr={[0, "foo"]} map={map} set={set} />
</main> </main>
</body> </body>
</html> </html>

View file

@ -1,7 +1,9 @@
import { expect } from '@playwright/test'; import { expect } from '@playwright/test';
import { testFactory } from './test-utils.js'; import { testFactory } from './test-utils.js';
const test = testFactory({ root: './fixtures/pass-js/' }); const test = testFactory({
root: './fixtures/pass-js/'
});
let devServer; let devServer;
@ -53,4 +55,24 @@ test.describe('Passing JS into client components', () => {
await expect(arrValue, 'is visible').toBeVisible(); await expect(arrValue, 'is visible').toBeVisible();
await expect(arrValue).toHaveText('0,foo'); await expect(arrValue).toHaveText('0,foo');
}); });
test('Maps and Sets', async ({ page }) => {
await page.goto('/');
const mapType = page.locator('#map-type');
await expect(mapType, 'is visible').toBeVisible();
await expect(mapType).toHaveText('[object Map]');
const mapValues = page.locator('#map-items li');
expect(await mapValues.count()).toEqual(2);
const texts = await mapValues.allTextContents();
expect(texts).toEqual(['test1: test2', 'test3: test4']);
const setType = page.locator('#set-type');
await expect(setType, 'is visible').toBeVisible();
const setValue = page.locator('#set-value');
await expect(setValue).toHaveText('test1,test2');
});
}); });

View file

@ -33,10 +33,10 @@ function convertToSerializedForm(value: any): [ValueOf<typeof PROP_TYPE>, any] {
return [PROP_TYPE.RegExp, (value as RegExp).source]; return [PROP_TYPE.RegExp, (value as RegExp).source];
} }
case '[object Map]': { case '[object Map]': {
return [PROP_TYPE.Map, Array.from(value as Map<any, any>)]; return [PROP_TYPE.Map, JSON.stringify(serializeArray(Array.from(value as Map<any, any>)))];
} }
case '[object Set]': { case '[object Set]': {
return [PROP_TYPE.Set, Array.from(value as Set<any>)]; return [PROP_TYPE.Set, JSON.stringify(serializeArray(Array.from(value as Set<any>)))];
} }
case '[object BigInt]': { case '[object BigInt]': {
return [PROP_TYPE.BigInt, (value as bigint).toString()]; return [PROP_TYPE.BigInt, (value as bigint).toString()];