Skip to content

fix: stabilize env config fetching #4364

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ async function openLocalhost(page: Page, port: number) {
// Wait for the page to load but don't wait for networkidle since env loading might be polling
await page.waitForLoadState('load');

// Wait for React to render - either the loading screen or the main content
await page.waitForSelector('body > div', { timeout: 10000 });
// Wait for the root element to be attached. It may not be visible immediately
// (for example, the remote app shows an empty #root while it loads env config),
// so we only wait for the element to exist in the DOM.
await page.waitForSelector('#root', { state: 'attached', timeout: 10000 });

// Log any errors found
if (pageErrors.length > 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import React, { createContext } from 'react';
import React, { createContext, useMemo } from 'react';
import Main from './Main';
import useFetchJson from '../hooks/useFetchJson';

export const EnvContext = createContext();

const App = () => {
const { data, loading, error, retry } = useFetchJson(
'/env-config.json',
{
maxRetries: 2,
retryDelay: 500,
timeout: 3000,
validateData: (data) => data && typeof data === 'object',
fallbackData: {
API_URL: 'https://fallback.api.com',
REMOTE_URL: 'http://localhost:3001/remoteEntry.js'
}
// Memoize options to avoid re-triggering fetch on each render in development
const fetchOptions = useMemo(() => ({
maxRetries: 2,
retryDelay: 500,
timeout: 3000,
validateData: (data) => data && typeof data === 'object',
fallbackData: {
API_URL: 'https://fallback.api.com',
REMOTE_URL: 'http://localhost:3001/remoteEntry.js'
}
);
}), []);

const { data, loading, error, retry } = useFetchJson('/env-config.json', fetchOptions);

if (loading) {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,18 +124,18 @@ const useFetchJson = (path, options = {}) => {
}, [fetchData]);

useEffect(() => {
// Component may mount twice in React 18 strict mode. Ensure the ref is
// reset so subsequent fetches can update state correctly.
isMountedRef.current = true;
fetchData();
}, [fetchData]);

// Cleanup on unmount
useEffect(() => {
return () => {
isMountedRef.current = false;
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
};
}, []);
}, [fetchData]);

return {
data,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import React, { createContext } from 'react';
import React, { createContext, useMemo } from 'react';
import Widget from './Widget';
import useFetchJson from '../hooks/useFetchJson';

export const EnvContext = createContext();

// Wraps the Widget component with the EnvContext
const WidgetWrapper = () => {
// Memoize fetch options to prevent repeated fetching in React strict mode
const fetchOptions = useMemo(() => ({
maxRetries: 3,
retryDelay: 1000,
timeout: 5000,
validateData: (data) => data && typeof data === 'object',
fallbackData: {
API_URL: 'https://remote.fallback.api.com'
}
}), []);

const { data, loading, error, retry } = useFetchJson(
`${__webpack_public_path__}env-config.json`,
{
maxRetries: 3,
retryDelay: 1000,
timeout: 5000,
validateData: (data) => data && typeof data === 'object',
fallbackData: {
API_URL: 'https://remote.fallback.api.com'
}
}
fetchOptions
);

if (loading) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,18 +125,19 @@ const useFetchJson = (path, options = {}) => {
}, [fetchData]);

useEffect(() => {
// Reset mount flag on each mount. React 18 strict mode mounts components
// twice in development, so without resetting this flag the fetch results
// would be ignored on the second mount.
isMountedRef.current = true;
fetchData();
}, [fetchData]);

// Cleanup on unmount
useEffect(() => {
return () => {
isMountedRef.current = false;
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
};
}, []);
}, [fetchData]);

return {
data,
Expand Down
Loading