Recoil icon indicating copy to clipboard operation
Recoil copied to clipboard

Feature request - destroy/reinitialize atom.

Open MiroslavPetrik opened this issue 3 years ago • 15 comments

Hello, thanks for recoil, simple & powerful!

I have a use case where I open a document, split it into atom tree, then I can modify the nodes of the document. If I decide that the changes are bad, I can discard the changes & start again by reading the document into recoil.

The trouble is that when I reopen the document, the app displays the cached state from previous steps, because recoil has the old values under the atom keys. So to get 'fresh atoms' I can choose new keys, which is bad since the previous will still consume memory. Other thing I do now, is that I pass the opened document through selector, where I have access to the reset api, which I apply to my document tree after I turn it into atoms, thus I set the atom values to the default (initial) value.

Also not sure how to handle this case properly, because now I'm getting lot's of warnings:

Duplicate atom key "node-c272f880-9518-4658-a33c-459a008889c9". This is a FATAL ERROR in
      production. But it is safe to ignore this warning if it occurred because of
      hot module replacement.

So maybe my approach is incorrect.

The error flow is:

// 1. initialize document into atoms 
const textAtom = atom({key: '1', default: {type: "TEXT", id: '1', value: "Hello world"});
// 2. update the node
const setText = useSetRecoilValue(textAtom);
setText("Bad text");
// 3. Reopen the document
// 4. Initialize the atoms (as step 1)
const textAtom = atom({key: '1', default: {type: "TEXT", id: '1', value: "Hello world"});
// 5. render app
const text = useRecoilValue(textAtom);
console.log(text) // "Bad text".

The workaround flow is:

// 5. reset atom in selector

export const persistedTextSelector = selector<Node>({
  key: "persistedTextSelector",
  get: ({get}) => get(persistedTextAtom),
  set: ({set, get, reset}, node) => {

     const textAtom = atom({key: node.id, default: node);
     set(persistedTextAtom, textAtom);
     reset(textAtom)
  }
})

// 6. render app
const text = useRecoilValue(persistedTextAtom);
console.log(text) // "Hello world".

What I would like to have is some flag on atom: atom({key: '1', default: {type: "TEXT", id: '1', value: "Hello world"},reinitialize: true})

But that still won't save me from getting the duplicate error warning. So I guess some Recoil.removeKeys([...ids]); api could work?

Thanks

MiroslavPetrik avatar Nov 22 '20 17:11 MiroslavPetrik

+1 a way to destroy atoms would be super helpful. Just resetting them is not always desired.

Svarto avatar Nov 27 '20 10:11 Svarto

same issue, I have two page's in my system, one list page and one editor page. I want to destory all atomFamily I created in editor page when leaving to list page. otherwise it will show changes again in atomFamily when reopening that editor page ( I hope to discard these changes)

xuzhanhh avatar Dec 01 '20 06:12 xuzhanhh

Note we are investigating garbage collection, so old unused atoms in a family would get cleaned up. But, that is not yet available.

You should not re-create an atom when trying to reset it, that will end up creating a new atom with the same key, which is why you are getting the error about a duplicate key. Likewise, you should not create atoms or do other side-effects inside the selector set.

If you want to reset your state you could write a hook, such as with useRecoilCallback(), which loops through your atoms and/or atom families and uses the reset() callback to reset them.

If you are happy resetting ALL of your state, you could use a fresh <RecoilRoot>, or apply a key prop to <RecoilRoot>. If you want to reset state to some known configuration when loading a new doc, maybe using a Snapshot and useGotoRecoilSnapshot() may work?

drarmstr avatar Dec 02 '20 20:12 drarmstr

I have a diff out for review for automatic collection of unused atoms.

If you have an indefinite set of atoms (e.g. one for each item in a document), you should use an atomFamily. Then it will automatically reuse the same atom when you give it the same parameter.

davidmccabe avatar Dec 11 '20 01:12 davidmccabe

could we use WeakMap to store data? I know now recoil use string as key, which isn't supported by WeakMap, but what if we use symbol. we automatically get the GC ability of javascript.

hyf0 avatar Feb 18 '21 12:02 hyf0

Symbols won't work for keys as they would not persist across multiple executions and WeakMap isn't sufficient for this use case. Something like FinalizationRegistry would be really convenient. Unfortunately, it doesn't have enough browser support yet to use. In the meantime we've added garbage collection functionality for this. It's currently behind a feature flag, but should be exposed in an upcoming release.

drarmstr avatar Feb 23 '21 01:02 drarmstr

@drarmstr is this feature flag in the public repo, or does it still need migrated from internal sources?

42shadow42 avatar Mar 14 '21 19:03 42shadow42

@davidmccabe , If I know that I will not need some particular set of atoms from the atomFamily (e.g. some particular parameter values of the atomFamily), how can I mark them for garbage collection? Is it the reset the proper way of marking the atoms for the garbage collection?

Am I right, that when I reset, I deallocate the data until the first read access. And only after the first access the default value is "loaded" into the atom?

dgrechka avatar Mar 15 '21 10:03 dgrechka

It should be in the nightly branch. There isn't a formal way to enable the feature flags with the open source build yet. But, if you're up for testing it then it might work to enable recoil_memory_managament_2020 in the map in Recoil_gkx.js... The interface may change before release.

drarmstr avatar Mar 16 '21 22:03 drarmstr

Is there any ETA on these changes being released? Just curious if we are looking at days/weeks/months. Thanks!

JoeDuncko avatar Mar 18 '21 15:03 JoeDuncko

@drarmstr Is there any ETA on GC? Can we help?

xotahal avatar May 20 '21 06:05 xotahal

cc @davidmccabe

drarmstr avatar May 20 '21 07:05 drarmstr

need this feature

ifokeev avatar Sep 10 '21 11:09 ifokeev

Came looking for this feature.

akaplatypus avatar Oct 25 '21 13:10 akaplatypus

Interested into this; also, for the sake of discussing it; we're using Recoil from outside React components (in Epics / Thunks), so would need the handling of atom be possible outside of React component IFF that use case is acceptable

sledorze avatar Nov 05 '21 10:11 sledorze