Instead of just returning a value from a recoil selector, you can return any promise, which means that you can do asynchronous functions in recoil selectors (this is a little like a redux thunk).
The huge bonus is that you don't have to do anything different when actually calling the selector - use can use useRecoilValue in the same way that you would from a normal selector.
The only thing you must do is somewhere in the component tree, wrap any component that uses an async selector with a <React.Suspense> tag with a fallback prop that will tell React what to display when the async selector isn't loaded yet.
Component:
import React from "react"; import { useRecoilValue } from "recoil"; import { highScores } from "./selectors"; const HighScore = () => { const score = useRecoilValue(highScores); return <div>High Score: {score}</div>; }; export default HighScore;
Selectors.js:
import { selector } from "recoil";
import { gameScore } from "./atoms";
const fetchHighScores = async () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(303);
}, 1000);
});
};
const highScores = selector({
key: "highScores",
get: async ({ get }) => {
return await fetchHighScores();
}
});
export { highScores };
Use:
import React from "react"; import "./styles.css"; import { RecoilRoot } from "recoil"; import HighScore from "./HighScore"; function App() { return ( <RecoilRoot> <div className="App"> <React.Suspense fallback={<div>Loading...</div>}> <HighScore /> </React.Suspense> </div> </RecoilRoot> ); } export default App;