Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions packages/plugin-rsc/e2e/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,58 @@ function defineTest(f: Fixture) {
)
})

test('React.cache API', async ({ page }) => {
await page.goto(f.url())
await waitForHydration(page)

// Verify React.cache section is present
const reactCacheSection = page.getByTestId('test-react-cache')
await expect(reactCacheSection).toBeVisible()

// Test cached data fetching - multiple components with same ID should share cache
const user1Elements = page.getByTestId('react-cache-fetch-user-1')
await expect(user1Elements).toHaveCount(2)

// Both user-1 elements should have the same cached timestamp
const user1Texts = await user1Elements.allTextContents()
expect(user1Texts[0]).toEqual(user1Texts[1]) // Should be identical due to caching

// Different user ID should have different data
const user2Element = page.getByTestId('react-cache-fetch-user-2')
await expect(user2Element).toBeVisible()
const user2Text = await user2Element.textContent()
expect(user2Text).toContain('User user-2')
expect(user2Text).not.toEqual(user1Texts[0])

// Test cached expensive computation
const testDataElements = page.getByTestId(
'react-cache-computation-test-data',
)
await expect(testDataElements).toHaveCount(2)

// Both test-data elements should have identical results
const computationTexts = await testDataElements.allTextContents()
expect(computationTexts[0]).toEqual(computationTexts[1])
expect(computationTexts[0]).toContain('Computed result for test-data')

// Different data should produce different results
const differentDataElement = page.getByTestId(
'react-cache-computation-different-data',
)
await expect(differentDataElement).toBeVisible()
const differentDataText = await differentDataElement.textContent()
expect(differentDataText).toContain('Computed result for different-data')
expect(differentDataText).not.toEqual(computationTexts[0])

// Verify React.cache is working by checking that function call counts are present
// and that caching behavior is working (identical results for same inputs)
expect(user1Texts[0]).toMatch(/Fetch calls: \d+/) // Should show some number of fetch calls
expect(computationTexts[0]).toMatch(/Computation calls: \d+/) // Should show some number of computation calls

// The key test: verify that React.cache is producing identical results for identical inputs
// This is the core functionality we want to test
})

test('hydration mismatch', async ({ page }) => {
const errors: Error[] = []
page.on('pageerror', (error) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react'

export function TestReactCache() {
return (
<div data-testid="test-react-cache" className="flex gap-2 p-2 border">
<div>
<h3>React.cache API Test</h3>
<ReactCacheDataFetch id="user-1" />
<ReactCacheDataFetch id="user-1" />
<ReactCacheDataFetch id="user-2" />
<ReactCacheExpensiveComputation data="test-data" />
<ReactCacheExpensiveComputation data="test-data" />
<ReactCacheExpensiveComputation data="different-data" />
</div>
</div>
)
}

// Global counters for tracking function calls
let fetchUserDataCallCount = 0
let expensiveComputationCallCount = 0

// Mock data fetching function
async function fetchUserData(id: string) {
fetchUserDataCallCount++
await new Promise((resolve) => setTimeout(resolve, 100)) // Simulate network delay
return {
id,
name: `User ${id}`,
email: `${id}@example.com`,
timestamp: new Date().toISOString(),
}
}

// Mock expensive computation
function expensiveComputation(data: string) {
expensiveComputationCallCount++
// Simulate expensive computation
let result = 0
for (let i = 0; i < 1000000; i++) {
result += i
}
return `Computed result for ${data}: ${result}`
}

// Create cached versions using React.cache
const cachedFetchUserData = React.cache(fetchUserData)
const cachedExpensiveComputation = React.cache(expensiveComputation)

async function ReactCacheDataFetch({ id }: { id: string }) {
const userData = await cachedFetchUserData(id)
return (
<div data-testid={`react-cache-fetch-${id}`} className="mb-2">
<strong>User:</strong> {userData.name} ({userData.email})
<br />
<small>
Fetch calls: {fetchUserDataCallCount} | Cached at: {userData.timestamp}
</small>
</div>
)
}

function ReactCacheExpensiveComputation({ data }: { data: string }) {
const result = cachedExpensiveComputation(data)
return (
<div data-testid={`react-cache-computation-${data}`} className="mb-2">
<strong>Result:</strong> {result}
<br />
<small>Computation calls: {expensiveComputationCallCount}</small>
</div>
)
}
2 changes: 2 additions & 0 deletions packages/plugin-rsc/examples/basic/src/routes/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { TestTailwindClient } from './tailwind/client'
import { TestTailwindServer } from './tailwind/server'
import { TestTemporaryReference } from './temporary-reference/client'
import { TestUseCache } from './use-cache/server'
import { TestReactCache } from './react-cache/server'
import { TestHydrationMismatch } from './hydration-mismatch/server'
import { TestBrowserOnly } from './browser-only/client'
import { TestTransitiveCjsClient } from './deps/transitive-cjs/client'
Expand Down Expand Up @@ -73,6 +74,7 @@ export function Root(props: { url: URL }) {
<TestModuleInvalidationServer />
<TestBrowserOnly />
<TestUseCache />
<TestReactCache />
</body>
</html>
)
Expand Down
Loading