Fixes hydration of Maps and Sets (#3960)
This commit is contained in:
parent
5fde2fd8bc
commit
ceda294e13
5 changed files with 50 additions and 5 deletions
5
.changeset/modern-bulldogs-burn.md
Normal file
5
.changeset/modern-bulldogs-burn.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fixes hydration of maps/sets
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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()];
|
||||||
|
|
Loading…
Reference in a new issue