atomFamily.remove() does not trigger re-renders for subscribed components #3143
Replies: 7 comments 24 replies
-
That's the designed behavior. Creating a new atom with atomFamily doesn't trigger rerender, and neither does removing. I think you want to look into the atom-in-atom pattern. |
Beta Was this translation helpful? Give feedback.
-
I find the behavior to feel bug-like. My specific issue is related to handling user sign outs - When the user signs out, all user-related state must be cleared in the UI. I had a related bug where the prior session state is still displayed in the UI, simply because nothing triggered the component to re-render when the family's param was reset. As soon as something else causes it to be re-rendered, the component will render the reset/default state, which is the real/current internal state of the atom. I've migrated away from using arrays of atoms (I think that's what you mean by atom-in-atom) to using atomFamily everywhere for a few reasons. It provides a simpler mental model because with families, atoms are created within the state graph automatically as components are rendered, rather than being created and managed manually within application logic. It's also much like database normalization and provides similar properties/benefits. For example with families it's easy to store metadata at the key or index level:
( In this example, I'm able to sync the |
Beta Was this translation helpful? Give feedback.
-
FYI, we plan to move atomFamily to a separate package. #2889 |
Beta Was this translation helpful? Give feedback.
-
You can already do this /**
* @returns the atom for the given param
* @sideeffect calls setState when the returned atom is removed
**/
function useAtomFamilyAtom<T extends string, U extends AnyAtom>(atomFamily<T, U>, param: T): U {
const [, setState] = useState(0)
const atom = atomFamily(param)
useEffect(() => {
return atomFamily.unstable_listen((e) => {
if (e.type === 'REMOVE' && e.atom === atom) {
setState((v) => v + 1)
}
})
}, [atomFamily, atom])
return atom;
} |
Beta Was this translation helpful? Give feedback.
-
remove() exists to provide a way to avoid memory leaks. Our mental model is that atomFamily is simply a |
Beta Was this translation helpful? Give feedback.
-
I see a few options here:
Option 6 is the best, IMO, for maintaining existing api, preventing memory leaks, and addressing stale references in React components. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Bug Description
When using
atomFamily.remove(param)
, components that are subscribed to the underlying atom do not re-render.Currently, a workaround is to set a noop/empty value before calling
remove()
, but this must be done carefully, and only works when the atom has a valid empty state.Reproduction Link
https://codesandbox.io/p/sandbox/busy-aj-hgjvhx
Beta Was this translation helpful? Give feedback.
All reactions