I'm new to Recoil.js, I have the following atom and selector for the signed-in user in the app:
const signedInUserAtom = atom<SignedInUser | null>({
key: 'signedInUserAtom',
default: null
})
export const signedInUserSelector = selector<SignedInUser | null>({
key: 'signedInUserSelector',
get: ({ get }) => get(signedInUserAtom),
set: ({ set, get }, newUserValue) => {
// ... Do a bunch of stuff when a user signs in ...
set(signedInUserAtom, newUserValue)
}
})
So basically I use signedInUserSelector
in order to set the new user.
Now, I want to have a few functions that will set the user through the selector, and use them across my components, like:
export async function signInWithGoogleAccount() {
const googleUser = async googleClient.signIn()
// here I need to set the user atom like:
// const [user, setUser] = useRecoilState(signedInUserSelector)
// setUser(googleUser)
}
export async function signInWithLocalAccount(email: string, password: string) {
const localUser = async localClient.signIn(email, password)
// here I need to set the user atom like:
// const [user, setUser] = useRecoilState(signedInUserSelector)
// setUser(localUser)
}
export async function signOut() {
await localClient.signOut()
// here I need to set the user atom like:
// const [user, setUser] = useRecoilState(signedInUserSelector)
// setUser(null)
}
The problem is since these functions are not defined inside components I can't use recoil hooks (like useRecoilState
to access selectors/atoms).
In the end I want to have any component to be able to do:
function SignInFormComponent() {
return <button onClick={signInWithGoogleAccount}>Sign In</button>
}
But how can I access selectors/atoms in signInWithGoogleAccount
if it is not in a component?
As I pointed out in another answer, you generally don't want to run into this, but if you eventually really need to update atoms outside of React Components you might give a try to Recoil Nexus.
In the same file where you have your RecoilRoot you'll have something like:
import React from 'react';
import { RecoilRoot } from "recoil"
import RecoilNexus from 'recoil-nexus'
export default function App() {
return (
<RecoilRoot>
<RecoilNexus/>
{/* ... */}
</RecoilRoot>
);
};
export default App;
Then, wherever you need to read/update the values:
import yourAtom from './yourAtom'
import { getRecoil, setRecoil } from 'recoil-nexus'
Eventually you can get and set the values like this:
// Read value
const loading = getRecoil(loadingState)
console.table({ loading })
// Set value
setRecoil(loadingState, true)
Or, if the new state depends on the previous one, it's recommended to use an updater function to correctly batch React state updates:
setRecoil(loadingState, loading => !loading)
That's it!
Disclaimer: I am the author of the library.
Check this CodeSandbox for a live example.