Recoil icon indicating copy to clipboard operation
Recoil copied to clipboard

How to keep list of entities atomFamily and entity atomFamily in sync?

Open wojtekmaj opened this issue 2 years ago • 3 comments

Suppose we have:

export const users = atomFamily({
  key: 'users',
  get: (params) => () => get(`/api/users?${stringify(params)}`);
});

export const user = atomFamily({
  key: 'user',
  get: (id) => () => get(`/api/users/${id}`);
});

and let's suppose I'd like to update user. It works just like I'd imagined:

const setUser = useSetRecoilState(user(userId));

…

setUser(…);

but now, users are outdated. This wouldn't be a problem if not for the fact users is atomFamily, not atom. So I thought, I should create some kind of map to keep my users there:

const userMap = atomFamily({
  key: 'userMap',
});

export const users = atomFamily({
  key: 'users',
  get: (params) => () => get(`/api/users?${stringify(params)}`);
});

export const user = atomFamily({
  key: 'user',
  get: (id) => () => get(`/api/users/${id}`);
});

but... how do I connect them so that:

  1. Fetching users will populate userMap with new/updated entities
  2. Fetching user will populate userMap with new/updated entity
  3. Updating user will populate userMap with updated entity
  4. Fetching users again will load data from userMap instead

?

I feel like it should be a commonly used pattern, but I'm really struggling to get to somewhere here. I'm always getting stuck on not being able to set() in get() (which would allow me to save the newly fetched users to userMap), which is tracked under #1118, but I understand there are limitations that would not allow this.

I've created a CodeSandbox to work on: https://codesandbox.io/s/recoil-entity-list-entity-sync-x00wx

wojtekmaj avatar Oct 15 '21 06:10 wojtekmaj

What about to keep just ids in users?

artygrand avatar Oct 29 '21 05:10 artygrand

I don't see a get method on atomFamily. Did that get changed to default?

I'm pretty sure in the case of pulling data from the server, you'd want the application to use selectors to access the data. Then you can define a setter that sets the updated data on the user atom and refreshes the users atom states from the server.

You could also use atom effects to update the server when the atom values change.

pc-erin avatar Nov 22 '21 16:11 pc-erin

I think I've finally figured it out!

https://codesandbox.io/s/recoil-entity-list-entity-sync-two-maps-uz36e?file=/src/store.js

This assumes local data always take priority, so if you update data locally, and fetch the same data from somewhere else, it's going to be still edited.

I have also created a version where remote data takes priority, so if you update data locally, and fetch the same data from somewhere else, it's going to be overwritten with the latest server version.

https://codesandbox.io/s/recoil-entity-list-entity-sync-two-maps-improved-sfo21?file=/src/store.js

Also @-ing @y120sb, @surajsharma, @mrmntte as they seemed to be interested in solution.

wojtekmaj avatar Nov 29 '21 13:11 wojtekmaj