Skip to content
This repository was archived by the owner on Jan 1, 2025. It is now read-only.
This repository was archived by the owner on Jan 1, 2025. It is now read-only.

[SSR] Async selectors suspend forever if used on server #1960

Closed
@st33v3

Description

@st33v3

I'm trying to use async selectors (those having get function return Promise) for data fetching during SSR (server side rendering), but it does not work - it sometimes hangs up and sometimes returns stalled value.

First quick question - are async selectors supposed to work in such situation?

Here is simplified code that I'm using:

import { Suspense } from "react";
import {
    atom,
    selector,
    useRecoilState,
    useRecoilValue,
    RecoilRoot
} from 'recoil';
import { Main } from "./Main";

function sleep(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

const filter =  atom({key: "filter", default: 0});

const query = selector({
    key: 'query',
    get: ({get}) => {
        const f = get(filter);
        return sleep(1000).then(() => {return f});
    },
});

const Query: React.FC = () => {
    const q = useRecoilValue(query);
    return <div>{q}</div>;
}

export const TestBody: React.FC = () => {
    const [f, setF] = useRecoilState(filter);
    return (
        <RecoilRoot>
            <div className="container-fluid">
                <div className="row">
                    <Main>
                        <p>
                            Filter: {f}
                        </p>
                        <button onClick={() => setF(f+1)}>Inc</button>
                        <hr/>
                        <Suspense fallback={<div>Wait</div>}>
                            <Query/>
                        </Suspense>
                    </Main>
                </div>
            </div>
        </RecoilRoot>
    );
};

query selector just delays value of filter atom, which is initially set to 0 and later changed by user interaction. During first request the rendering never ends. I found that call to useRecoilValue in Query component throws Promise as expected, but the promise never settles, although promise in selector definition settles after 1s. In browser, the promise settles correctly, so I suspect this is a bug in Recoil (in SSR environement). Or am I doing something wrong?

During any subsequent request query returns value (0) without waiting. I assume that this is cached value that first request should return. This behavior difference in first and other request is strange. As far as I understood how Recoil works, it should store atoms and selectors to recoil root and the root should be fresh for each request. How can one request affect another? I would expect that TestBody component would be evaluated in each request with same result.

Additional information:

  • I'm serving server response via renderToPipeableStream which should support Suspense
  • React version: 18.2.0
  • Recoil version: 0.7.5

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions