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

useEffect is never invoked in suspended component #18749

Closed
JoseExposito opened this issue Apr 26, 2020 · 5 comments
Closed

useEffect is never invoked in suspended component #18749

JoseExposito opened this issue Apr 26, 2020 · 5 comments
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug

Comments

@JoseExposito
Copy link

I'm trying to Fetch-On-Render (inside a useEffect hook) some data that depends on my component's props.

My component tree is wrapped in a Suspense component and the component that fetches data is a couple of levels down the tree.

Because the useEffect hook is in a suspended component it never gets invoked.

I'm not sure is this is the expected behaviour or a bug.

React version:

Experimental:

  "react": "0.0.0-experimental-e5d06e34b",
  "react-dom": "0.0.0-experimental-e5d06e34b",

Steps To Reproduce

Link to code example:

https://codesandbox.io/s/practical-burnell-75rqm?file=/src/index.js

import React, { Suspense, useEffect } from "react";
import ReactDOM from "react-dom";

class Resource {
  status = "pending";
  result = null;
  suspender = new Promise(resolve => {
    this.resolve = resolve;
  });

  read() {
    if (this.status === "pending") {
      throw this.suspender;
    } else {
      return this.result;
    }
  }

  load() {
    console.log("This method is never invoked");
    this.result = "Profile loaded";
    this.status = "success";
    this.resolve();
  }
}
const resource = new Resource();

function ProfilePage() {
  return (
    <Suspense fallback={<h1>Loading profile...</h1>}>
      <ProfileDetails />
    </Suspense>
  );
}

function ProfileDetails() {
  useEffect(() => {
    console.log("This code is never reached");
    resource.load();
  }, []);

  const user = resource.read();
  return <h1>{user}</h1>;
}

const rootElement = document.getElementById("root");
ReactDOM.createRoot(rootElement).render(<ProfilePage />);

The current behavior

The useEffect hook in ProfileDetails is never invoked and the app stays in a loading state forever.

The expected behavior

I'd expect the hook to be called and the data to be loaded.

At the moment the only solution is to move all the data fetching logic outside the Suspense component, in my example to ProfilePage

@JoseExposito JoseExposito added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Apr 26, 2020
@vkurchatkin
Copy link

I'm not sure is this is the expected behaviour or a bug.

This is expected. useEffect is triggered after a component is rendered and committed. If it is suspended, it's not going to happen

@gaearon
Copy link
Collaborator

gaearon commented Apr 26, 2020

With fetch-on-render, you have to have the read itself kick off the fetch. (Which is why fetch-on-render isn’t great, generally speaking — for example, it leads to waterfalls with nested components.)

Indeed, if a component's rendering suspends, any effects of that render are discarded. Effects only run for successful renders that haven't been suspended.

@gaearon gaearon closed this as completed Apr 26, 2020
@JoseExposito
Copy link
Author

JoseExposito commented Apr 26, 2020

Thank you very much for your quick answers @vkramskikh and @gaearon and for the technical explanation.

I'll have to move my fetching logic to the top level component or figure out a way to access React Router props and context to render-as-I-fetch.

@gaearon
Copy link
Collaborator

gaearon commented Apr 26, 2020

There are no good ergonomic solutions for render-as-you-fetch without either manually hoisting data fetching logic upwards (annoying) or letting a compiler do it (like Relay does). We're working on something in that vein but it's very early so we don't have anything to share yet.

@jezzgoodwin
Copy link

Hi, sorry to comment on a closed ticket, but I'm not sure where to ask this.

@gaearon have you guys considered making a useSuspense(val: boolean) hook which could tell the Suspense component above to show the fallback, while still loading the component? This would seem like the best of both worlds as you'd still get the power of Suspense but also be able to fetch data within your component.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug
Projects
None yet
Development

No branches or pull requests

4 participants