-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
How to set a new state using async API call #149
Comments
Hi @otaviobps. Similar to const [teams, setTeams] = useRecoilState(teamsState);
const addTeam = useCallback((team) => {
api.addTeam(team).then(addedTeam => {
setTeams(teams => teams.concat(addedTeam));
});
}, [setTeams]); |
Hey @acutmore thanks for replying! Well, I'm entering a recursion problem, because I don't have an atom and I'm not sure what to call on the
The documentation states that to create an async state, the way is creating a
But since my Recoil state was made with a |
تزكروني باني احبكم جدا |
هااي |
@otaviobps - A few notes: Your This line:
won't work because the selector is trying to set itself from its set function, which would be an infinite loop. the In general, selectors are intended to be pure functions for derived state. Given a set of inputs, a selector should always evaluate to the same result and may be cached internally. I suspect your For the situation of syncing mutable remote state with local Recoil state, consider using an atom. The atom can represent the local copy of the state and effects can be used to subscribe either remote or local changes to keep them in sync. Please refer to the example in #143 . I'll be updating the online docs to make these two use-cases of async queries more clear, please provide feedback if the example in #143 helps. |
Duplicate of #143 |
Hi @otaviobps.
Good question. This is why Recoil has Atoms and Selectors. Atoms have state, but no logic. Selectors have logic but no state. If you want async state you need to combine an Atom and Selector together. (maybe @drarmstr will be able to correct me here). const state = atom({
key: "state",
default: null,
});
export const asyncState = selector({
key: "async-state",
async get({ get }) {
const s = get(state);
if (s !== null) {
return s;
}
return await api.get("/default");
},
set({ set }, newValue) {
set(state, newValue);
}
}); The above pattern only works for simple use-cases. If you had two web pages open and they were both updating the server state, or Recoil was persisting state between page-loads then the client could easily get out of sync with the server. In these situations you need a way of listening to remote state changes and updating recoil to ensure they stay in sync. More information in #143. |
Hi @acutmore and @drarmstr. Thank you for your both replies.
In fact it worked great. Maybe was luck, but I'll refactor it to use the
How exactly would I initialize the @acutmore for what I'm reading in issues and the documentation, it's not a good practice to Edit: Just to clarify, |
Hi @otaviobps
Correct you can't put an async API call inside an atom, you'll need a component to set the initial value of the atom. e.g. by fetching the state in a If you want to delay loading Recoil until after the Atom has been initialised the function AppLoader() {
const [defaultValue, setDefaultValue] = useState(null);
useEffect(() => {
fetch("/teams").then(teams => setDefaultValue(teams));
}, []);
if (defaultValue === null) {
return "loading";
}
function initializeState({ set }) {
set(teamsState, defaultValue);
}
return (
<RecoilRoot initializeState={initializeState}>
<App />
</RecoilRoot>
);
} Keeping databases in sync over a network connection is a difficult problem. I would be more tempted to use a library that has this as a core feature. https://pouchdb.com/ for example. And then use Recoil as a way for React components to access / respond / interact with the data. |
Atom defaults can also accept selectors, if you want to initialize the value based on a query. And yes, feel free to use standard React effects or other libraries to sync remote state and then wrap that with Recoil nodes to integrate with Recoil's data-flow graph for updating and rendering React components. |
Duplicate of #143 |
How do you use this to add to state? What's the reason for having a 'get' async call when I can just make a get request without having a selector. How is this 'get' storing the items to state? if it isn't, what Is this doing? If I need to do a simple request for an array of users, and store that in recoil state. while being able to check if data is loading or hasErrored. How do I do it? I can't find where it explains this in the docs, I've only found explanation related to using 'get' of the selector. Now I know you can use an atom to store this in state within a useEffect hook. But this is basic and does not allow you to check if the thing has loaded or has err'd. |
Lots of questions, I'll do my best to answer some of them.
Selectors will cache the returned value. If it is re-subscribed to later a new You can reverse around the order so the result is stored in the atom, like this: const asyncDefault = selector({
key: "asyncDefault",
async get() {
return await api.get("/default");
}
});
export const stateWithAsyncDefault = atom({
key: "stateWithAsyncDefault",
default: asyncDefault,
}); |
Hello, type Pager = {
ids: string[];
totalAmount: number;
};
const pagerAtom = recoil.atom<Pager>({
key: 'pagerAtom',
default: {
totalAmount: 999, // some value I get from initial json object
ids: []
}
});
type PageSelectorOptions = {
offset: number;
pageSize: number;
};
const pageSelector = recoil.selectorFamily<string[], PageSelectorOptions>({
key: 'pageSelector',
get: ({offset, pageSize}) => async ({get}) => {
const pager = get(pagerAtom);
const availableAmount = Math.min(pager.totalAmount - offset, pageSize);
const ids = pager.ids.slice(offset, offset + pageSize);
if (ids.filter(Boolean).length >= availableAmount) {
return ids;
}
const remotePager = await fetchPager({offset, pageSize});
// this is the point I need to set fetched data to pagerAtom
// set(pagerAtom, (prev) => {
// const nextIds = [...prev.ids];
// remotePager.ids.forEach((id, index) => nextIds[offset + index] = id);
// return {
// totalAmount: remotePager.totalAmount,
// ids: nextIds
// };
// });
return remotePager.ids;
}
}); |
I'm trying to implement basic features to a project using Recoil, just to test the library. I'm really liking what I'm seeing. However, I have a doubt regarding to updating a state based on an async API call, i.e., I created the following
selectors
to fetch from API:The
teamsState
fetch a list ofteams
from the API, and theteamById
returns the selectedteam
from the list of teams.My doubt comes now. If I need to add a new team, how should I proceed?
I have a route to add a team in my backend, and the response is the object team I added, not a list of teams. So, how should I update the
teamsState
to reflect this added object? Should I get all teams again, making an API call to get the list of teams (this doesn't seem the right choice, but would work)?Great work on bringing this new library to the React environment!
The text was updated successfully, but these errors were encountered: