Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions playground/react/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { CountProvider } from './context/CountProvider'
import { ContextButton } from './context/ContextButton'
import { TestImportAttributes } from './import-attributes/test'
import { TEST_NON_JSX, TestNonJsx } from './non-jsx/test'
import { ReactCacheTest } from './cache/ReactCacheTest'

function App() {
const [count, setCount] = useState(0)
Expand Down Expand Up @@ -50,6 +51,7 @@ function App() {
<TestImportAttributes />
{TestNonJsx()}
{TEST_NON_JSX()}
<ReactCacheTest />
</div>
)
}
Expand Down
62 changes: 62 additions & 0 deletions playground/react/__tests__/react.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,68 @@ test('import attributes', async () => {
expect(await page.textContent('.import-attributes')).toBe('ok')
})

test('React.cache API availability', async () => {
// Test that the React.cache component renders
expect(await page.textContent('.react-cache-test h3')).toBe(
'React.cache Test',
)

// Test React.cache and React.use are available in React 19
expect(await page.textContent('#react-cache-available')).toBe(
'React.cache available: Yes',
)
expect(await page.textContent('#react-use-available')).toBe(
'React.use available: Yes',
)

// Test that we can create cached functions
expect(await page.textContent('#api-test-result')).toContain(
'Success - created cached function of type: function',
)
})

test('React.cache synchronous behavior', async () => {
// Test synchronous cache behavior
await expect.poll(() => page.textContent('#sync-result1')).toBeTruthy()
await expect.poll(() => page.textContent('#sync-result2')).toBeTruthy()

const result1 = await page.textContent('#sync-result1')
const result2 = await page.textContent('#sync-result2')

// Verify both calls return the same value (indicating caching)
expect(result1).toBe(result2)
expect(await page.textContent('#sync-results-equal')).toBe(
'Results equal: true',
)
})

test('React.cache with async operations', async () => {
// Wait for suspense to resolve
await expect.poll(() => page.textContent('#async-result')).toBeTruthy()

// Verify async cache result loads
const asyncResult = await page.textContent('#async-result')
expect(asyncResult).toContain('Async result')
})

test('React.cache re-render behavior', async () => {
// Get initial call count
const initialCallCount = await page.textContent('#sync-call-count')

// Force a re-render
await page.click('#cache-test-rerender')

// Wait for re-render to complete
await expect
.poll(() => page.textContent('#cache-test-rerender'))
.toContain('count: 1')

// Check that results are still equal after re-render
expect(await page.textContent('#sync-results-equal')).toBe(
'Results equal: true',
)
})

if (!isBuild) {
// #9869
test('should only hmr files with exported react components', async () => {
Expand Down
124 changes: 124 additions & 0 deletions playground/react/cache/ReactCacheTest.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import React, { useState } from 'react'

let callCount = 0

// Create a cached function that should demonstrate React.cache behavior
const cacheFn = React.cache(() => {
callCount++
return `Called ${callCount} times`
})

// Create an async cached function for demonstration
const asyncCacheFn = React.cache(async () => {
await new Promise((r) => setTimeout(r, 10))
return `Async result`
})

export function ReactCacheTest() {
const [renderKey, setRenderKey] = useState(0)

return (
<div
className="react-cache-test"
style={{ padding: '20px', border: '1px solid #ccc', margin: '10px' }}
>
<h3>React.cache Test</h3>
<div id="react-version">React version: {React.version}</div>
<div id="react-cache-available">
React.cache available:{' '}
{typeof React.cache === 'function' ? 'Yes' : 'No'}
</div>
<div id="react-use-available">
React.use available: {typeof React.use === 'function' ? 'Yes' : 'No'}
</div>

<button
id="cache-test-rerender"
onClick={() => setRenderKey((k) => k + 1)}
style={{ padding: '8px 16px', margin: '10px 0' }}
>
Force Re-render (count: {renderKey})
</button>

<button
id="cache-test-reset"
onClick={() => {
callCount = 0
setRenderKey((k) => k + 1)
}}
style={{ padding: '8px 16px', margin: '10px 0' }}
>
Reset Call Count
</button>

{/* Test API availability */}
<ApiAvailabilityTest />

{/* Test synchronous cache behavior */}
<SyncCacheTest />

{/* Test async cache with error boundary */}
<React.Suspense
fallback={<div id="cache-loading">Loading async test...</div>}
>
<AsyncCacheTest />
</React.Suspense>
</div>
)
}

function ApiAvailabilityTest() {
let result = 'unknown'
try {
const testCache = React.cache(() => 'test')
result = `Success - created cached function of type: ${typeof testCache}`
} catch (error) {
result = `Error: ${error.message}`
}

return (
<div
id="api-test"
style={{ padding: '10px', background: '#f0f0f0', margin: '10px 0' }}
>
<h4>API Availability Test</h4>
<div id="api-test-result">Cache creation: {result}</div>
</div>
)
}

function SyncCacheTest() {
// Call the cached function multiple times within the same render
const result1 = cacheFn()
const result2 = cacheFn()

return (
<div
id="sync-test"
style={{ padding: '10px', background: '#e8f4f8', margin: '10px 0' }}
>
<h4>Synchronous Cache Test</h4>
<div id="sync-result1">First call: {result1}</div>
<div id="sync-result2">Second call: {result2}</div>
<div id="sync-results-equal">
Results equal: {result1 === result2 ? 'true' : 'false'}
</div>
<div id="sync-call-count">Total function calls: {callCount}</div>
</div>
)
}

function AsyncCacheTest() {
// Use React.use to consume async cached function - don't use try/catch
const asyncResult = React.use(asyncCacheFn())

return (
<div
id="async-test"
style={{ padding: '10px', background: '#f0f8e8', margin: '10px 0' }}
>
<h4>Async Cache Test</h4>
<div id="async-result">Async result: {asyncResult}</div>
</div>
)
}
Loading