question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Question: how best to manage atom families?

See original GitHub issue

Scenario 1, forms with lots of fields: I have this common problem where I have a form with lots of fields. At first I think an Atom Family fills this need. I’ll set each atom in the family to one of the key value pairs of my form. But in order for that to work, I need to also have an atom that tracks all the keys in the form–otherwise how do I know what all my fields are? (There’s no way to get all the values out of a family.) That’s fine I guess, but it feels kinda clunky having two pieces of state that represent the same thing and need to be kept in sync. The reason I want to use an atom family in the first place is that when I update one value in my form, I don’t want all the form elements to update–only the one that updated should re-render. I also want there to be an easy way to get the form back as a single object so I’ve tried using selectors to combine the atom that has all the keys with the atom family, but I have to be careful not to use that selector higher up in the component hierarchy or else the entire tree will re-render when the selector reevaluates when any of the atoms update.

Question: What’s the best way to manage the keys in the form? Or just, what’s the best way to manage a form in general? Is having a companion atom with an array of its keys the best solution?

Scenario 2, asynchronously adding/removing from a family: Another (similar) problem I have is a game where players can join a lobby. I want to store all the player objects in an atom family, keyed by their id. I thought atom effects would be perfect and I could subscribe each atom to websocket updates for its own player object, but the problem I’ve run into is… how do new atoms get added to the family? I receive a websocket message saying a player joined, but I can’t do a useRecoilState(playerState(newPlayer.id)) inside of that handler for the new atom because of the rules of hooks. And I don’t believe atom effects will solve my problem because (as far as I know) you can’t use them to add members to a family. How am I supposed to do this?

One (very bad) idea I had was to store the new player data from the websocket handler into local component state, then make a child component that accepts the new player object as a prop and then accesses its own atom from the family with a hook, but that feels super jank. I’d have something like:

function ComponentWhoseOnlyPurposeIsCreatingAnAtom({newPlayer}) {
  const [player, setPlayer] = useRecoilState(playerFamily(newPlayer.id));

  useEffect(() => {
    setPlayer(newPlayer);
  }, [newPlayer]);

  return null; // don't render anything?
}

Then at that point, I have a similar solution to the form example where I have an atom that stores all the player IDs (the keys for its counterpart family of players), and I just add the new ID to that. This solution feels ridiculous and hacky, but I do think it would probably work eventually.

Question: How do I add new members to a family one at a time? Feels weird to have to put something in local state first, so it can go into a prop, so it can be used with a hook. In the form example I at least know all the fields I’ll have ahead of time. What about when it’s async?

I have a feeling that my trouble has to do with me not “thinking in Recoil” or something. Apologies if this is confusing.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:4
  • Comments:8 (3 by maintainers)

github_iconTop GitHub Comments

3reactions
tonyabracadabracommented, Nov 12, 2020

Yes, for Scenario 1 it is a common pattern to use an atomFamliy() for things like field entries in a form alongside a list of field names. That list may be static or could also be dynamic state based on your usage model. This allows for limiting re-renders only for components that subscribe to specific changing fields. As you outline, you can use a selector to merge/set the individual fields, but it will subscribe to all changes then.

For your follow-up question one option could be to explicitly reset the atoms when unmounting the component. Another option could be to add another family parameter with whatever uniquely identifies the form state. So, for example, if you have form state per player in a game the key might be {field: string, playerID: number}.

For Scenario 2 it is true that you can’t use an atom effect to create a new atom. That can be done by subscribing to websockets and then creating the atom. While the new atoms should be created relative to a React context, you don’t need to make a component that is specific to the new player. For example, you could create a callback for your websocket subscription with useRecoilCallback() that can set, and thus create, the new atoms. That still requires some component to manage that subscription, though. If you want to avoid that entirely, here’s a potential pattern: you could have a single atom that manages the list of current players that uses an atom effect to subscribe to the websocket for new players and updates itself with the current player list and then save the initial player data to be then used by an effect in the atom family of player data to initialize itself. (or leverage #707 when available)

What if I have a lof of players and is it still okay for me to manage a list of current players with just a single atom? Will there be any significant performance loss if the number of players is large?

1reaction
drarmstrcommented, Nov 12, 2020

What if I have a lof of players and is it still okay for me to manage a list of current players with just a single atom? Will there be any significant performance loss if the number of players is large?

You can store large structures in atoms. One significant factor when deciding the granularity of atoms is the granularity you want for subscriptions. If the actual user data is stored in a separate atom family, then changes in that would impact components for other users. If the set of users doesn’t change much, then it shouldn’t cause too many re-renders. But, if it does change a lot and a lot of components use that atom, then that could be a concern.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Atom Family in Recoil for Statement Management — Nextjs
This blog post will teach you how to use the recoil atom family to update the specific atom in recoil state management.
Read more >
Is there a proven way to manage a collection of atoms in Recoil?
You can use atomFamily to manage your posts. You can use another atom to manage the post ids, if you want to add...
Read more >
atomFamily(options) - Recoil
An Atom Family represents a collection of atoms. When you call atomFamily() it will return a function which provides the RecoilState atom based...
Read more >
Sharanya Saxena - Copy of Ionic Bonds SE - Name - Studocu
Question : How are elements arranged into chemical families? Observe: Drag the nonmetal into the trash ( ) so there is only the...
Read more >
13 Atom Tips & Shortcuts To Improve Your Workflow (Images ...
I'm going to show you the best 13 tips, tricks, and shortcuts to improve your Atom ... Atom comes with a nice default...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found