Implementation Question (with codesandbox) #1327
-
Code sandbox: https://codesandbox.io/s/jovial-mcnulty-b3n5y?file=/src/state/state.ts:290-648I have a few pieces of data and am struggling to wrap my head around how to represent this with atoms/selectors. The example I have is an editable favorites list for a user. Both the user and favoriteItem are separate objects: interface User {
id: string;
name: string;
}
interface Favorite {
id: string;
contentId: string;
userId: string;
} In order to fetch the list of favorites, I have to know the User ID. I've planned on using a selector to do so, as follows: const currentUserState = atom({
key: 'currentUser',
default: { id: 'test', name: 'user' }
});
const currentUserFavoriteList = selector({
key: 'favoritesList',
get: async ({get}) => {
try {
return await getUserFavorites(get(currentUserState));
} catch(e) {
return [];
}
},
}) I can use the currentUser with useRecoilState and the selectors make sense, at least to fetch the data. My issue lies with how to update the current user favorites list (adding or removing a favorite). I've tried using the "set" attribute with the selector and haven't had any luck updating the rendered components. Is there something I'm missing? Is there a better pattern to use for this scenario with atoms and selectors? The tough part is the favorites list state is more or less derived from the user state, but should be updated independently of the user changing. I'd also like to send an API call in the "set" function to update the server with the new state of the favorites list. When is use the set function, I end up getting an infinite recursion (duh - but thats how I thought to do it 😃) // ./state/state.ts
export const currentUserFavoriteList = selector({
key: "favoritesList",
get: async ({ get }) => {
try {
const user = get(currentUserState);
if (!user) return [];
return await getUserFavorites(user.id);
} catch (e) {
return [];
}
},
// infinite recursion, max call size exceeded
set: ({ set }, newValue) => {
set(currentUserFavoriteList, newValue);
}
}); Otherwise, I get a "attempted to set a read only value". // ./state/state.ts
export const currentUserFavoriteList = selector({
key: "favoritesList",
get: async ({ get }) => {
try {
const user = get(currentUserState);
if (!user) return [];
return await getUserFavorites(user.id);
} catch (e) {
return [];
}
},
}); |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
I've figured out the issue in my case and have updated the codesandbox here. The solution I found was to memoize a "userFavoritesWithId" atom generating function like so: const getUserFavoritesList = async (userId: string | undefined) => {
try {
if (!userId) return [];
return await getUserFavorites(userId);
} catch (e) {
return [];
}
};
export const userFavoriteListFromId = memoize((userId: string | undefined) =>
atom({
key: `userFavoritesList_${userId}`,
default: getUserFavoritesList(userId)
})
); By doing this, I was able to implement adding and removing items from the list, and the favorites list will be scalable to any other users as long as they have unique IDs. const addToFavoritesList = () => {
if (!currentUser) return;
setFavoritesList([
...favoritesList,
{
id: `item-${index}`,
userId: currentUser.id,
contentId: `content-${index}`
}
]);
setIndex((cur) => cur + 1);
};
const removeFromFavoritesList = () => {
if (!currentUser) return;
// IMPORTANT!
// do the array copy into a new variable first - since atom states are read only, trying to do
// favoritesList.pop() will throw an
const toSet = [...favoritesList];
toSet.pop();
setFavoritesList(toSet);
setIndex((cur) => cur - 1);
}; |
Beta Was this translation helpful? Give feedback.
I've figured out the issue in my case and have updated the codesandbox here. The solution I found was to memoize a "userFavoritesWithId" atom generating function like so:
By doing this, I was able to implement adding and removing items from the list, and the favorites list will be scalable to any other users as long as they have un…