[SSR] Async selectors suspend forever if used on server #1960
Description
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