Skip to content
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

[React 19] infinite loop when using React lazy & functional formAction together. #29930

Open
Henry-Hong opened this issue Jun 19, 2024 · 3 comments

Comments

@Henry-Hong
Copy link

Henry-Hong commented Jun 19, 2024

Summary

Version

"react": "19.0.0-rc-107a2f8c3e-20240617",
"react-dom": "19.0.0-rc-107a2f8c3e-20240617"
OS : windows 11
Chrome : 125.0.6422.142

Overview

We have a great feature 'functional form action' in react 19.
<form action={functionalFormAction}

but with React lazy, it doesn't seem to work properly.
It causes infinite loop depends on its use cases.

image


  1. case1 : infinite loop (React lazy without <Suspense/>. All dynamic codes are inside form component)
  2. case2 : no infinite loop (React lazy with <Suspense/>. All dynamic codes are inside form component)
  3. case3 : infinite loop (React lazy with <Suspsne /> & has dependency on useFormStatus. All dynamic code are inside form component)

Related Issues

#29235 Bug: Nested lazy components cause rerendering
#27573 Bug: Nested components imported with React.lazy cause rerendering

Reproduction

Go to CodeSandBox
Try Case2 at first, (submit form by clicking button)
Try Case3 at second, (it causes infinite loop so it makes js blocked)
Try Case1 at the last (it causes infinite loop so it makes js blocked)

Preview

App.tsx
image

Case1.tsx
image

Case 1 Result
image

Case 2 Result
image

Case 3 Result
image

@eps1lon
Copy link
Collaborator

eps1lon commented Jun 25, 2024

You are creating a new, lazy component within render. The component type is therefore changing on every render. Then React has to re-render when the lazy resolves but encounters a new lazy component since you called lazy() again. This is expected behavior.

Do you still enter an infinite loop if you move the component creation outside of render?

+const TargetComponent = lazy(...)

 function Case() {
-  const TargetComponent = lazy(...)
   return <TargetComponent />
 }

I'd recommend splitting these up into different repros. It's hard to follow how you use the repro and what you're actually reproducing.

@Henry-Hong
Copy link
Author

Henry-Hong commented Jun 28, 2024

@eps1lon

Do you still enter an infinite loop if you move the component creation outside of render?

I was able to prevent infinite loop for 'Case 1'. But unfortunately, 'Case 3' is still suffering from infinite loop.

And, moving the React.lazy outside of render has limits. Due to the inability to utilize component props, 'React.lazy' can only be used statically.

for example, I have an component that takes file names as props and renders dynamically based on them. According to the proposed solution, this code can no longer be used.

image

@eps1lon
Copy link
Collaborator

eps1lon commented Jun 28, 2024

And, moving the React.lazy outside of render has limits. Due to the inability to utilize component props, 'React.lazy' can only be used statically.

You need to implement a client cache then. React doesn't have that capabiltiy at the moment. Unconditionally creating React.lazy during render is not supported. Just like you couldn't create a new Promise during render and pass it to use.

It's probably better to use some form of codegen to create the icons anyway so that all available icons are known statically.

I was able to prevent infinite loop for 'Case 1'. But unfortunately, 'Case 3' is still suffering from infinite loop.

Do you have a repro just for that case with the React.lazy created outside of render?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants