Recoil: Questions from first-use

Created on 21 May 2020  路  4Comments  路  Source: facebookexperimental/Recoil

I used Recoil.js today on a livestream and came up with a few questions related to behavior that didn't make sense and was potentially buggy.

I don't want to tweet all these questions, but if Twitter is the right place to ask, I'll do it there instead.

  1. How do you unregister an atom from Recoil's state? Isn't this a possible situation for a memory leak?
  2. There is there an issue with hot-reloading where after reloading, the state setters don't force components to update. No clue what's going on here.
  3. I'm getting a warning when using get in select's get prop function: "Warning: Cannot update a component from inside the function body of a different component". I'm assuming this is because I'm updating Recoil state in Component1 and Component2 is reading that value.
  4. Memoization of selector values is causing strange issues. I was trying to calculate a new state in my derived state based on the previous state kinda like a reducer. Since one wasn't available, I went ahead and created a count variable outside the selector and whenever the get function caused the selector to reload, it would update the count. Sadly, because of memoization, the count changed in unexpected ways. So my real question is how do I set a state based on a previous value using a selector?
  5. I didn't realize selectors were storing literally every state update as a new memoized value. Isn't that a possible area for memory leaks?
  6. I have general problems with the nomenclature in general (these are my personal opinions. I understand if they might not fit with your project goals):

    • It's a bit confusing having hooks that essentially do the same thing: useRecoilState, useSetRecoilState, useRecoilValue. I get the reasons behind having different ones, but it's too confusing to use your library for newcomers when they have to learn little intricacies like this.

    • Naming both derived states and atoms as somethingState in your examples is confusing because derived states only sometimes have a setter. If I was developing a larger application, I would have no clue which is which.

    • The selector function should probably be named derivedState or derivedAtom or computedState right?

    • The get function in the callback of the get prop in a selector is technically the state selector function. I get if you have a set function, it makes sense to call it get, but it could be select and change or update. Doesn't have to be get and set.

    • The name for atom is really stateSegment or stateContainer.

    • Since Recoil is essentially the same as every other state library, I would expect to see names with people are familiar. I'm familiar with selector, getState, dispatch, and setState, but not necessarily atom and the way selector is used.

  7. The set function isn't defined in the docs for selector unless you go to the "core" section.
question

Most helpful comment

I've had an almost opposite experience picking up this library, funny how things work like that.

I can't really speak to the memory leak questions, as I'm new to this just as you are.

This is my take though:

How do you unregister an atom from Recoil's state?

I'm not sure what you mean by this, are you wanting to reset the atom? You can do that by setting the atom to DefaultValue or calling the .reset method in the selector set function.

There is there an issue with hot-reloading where after reloading, the state setters don't force components to update. No clue what's going on here.

They mentioned that in another issue, they're aware of it, but it won't be fixed for a bit. I believe @davidmccabe is refactoring part of the library that will resolve this issue.

I'm getting a warning when using get in select's get prop function: "Warning: Cannot update a component from inside the function body of a different component". I'm assuming this is because I'm updating Recoil state in Component1 and Component2 is reading that value.

I've not run into this issue, but I can confirm that updating recoil state from one component and reading it in another isn't an issue.

how do I set a state based on a previous value using a selector?

I believe this can be done in two ways:

  • useState or useSetState accept a 'T -> 'T -> unit overload (compared to the normal 'T -> unit) that feeds in the current value.
  • The set method for selectors have an overload to feed in the previous value similar to thie above point.

I have general problems with the nomenclature

I'm not using this library natively as I'm writing bindings to use it from F#, so I'm able to change the naming a little bit, but for the most part I quite like it.

I do think most of these concerns will be resolved as the documentation is finished/polished.

It's a bit confusing having hooks that essentially do the same thing: useRecoilState, useSetRecoilState, useRecoilValue. I get the reasons behind having different ones, but it's too confusing to use your library for newcomers when they have to learn little intricacies like this.

I'm a big fan of being able to only have one component subscribe to the value of an atom and yet allow another to set the values. It's a great performance increase.

Most of the hooks follow a very familiar pattern via React, I personally found it quite easy to pick up and run with it.

Naming both derived states and atoms as somethingState in your examples is confusing because derived states only sometimes have a setter. If I was developing a larger application, I would have no clue which is which.

I definitely agree here, I made all values returned from atoms and selectors in my bindings as RecoilValue with type restrictions to determine if it's settable: RecoilValue<'T,ReadOnly> and RecoilValue<'T,ReadWrite>.

The get function in the callback of the get prop in a selector is technically the state selector function. I get if you have a set function, it makes sense to call it get, but it could be select and change or update. Doesn't have to be get and set.

I have mixed feelings on this point. get and set are very straightforward, but they don't quite describe what it's doing. This is mostly because they're either getting/setting, mapping, or binding the value.

Since Recoil is essentially the same as every other state library, I would expect to see names with people are familiar. I'm familiar with selector, getState, dispatch, and setState, but not necessarily atom and the way selector is used.

I think this may be part of why you're having some issues, I'd argue it's very much not like every other state library, as it seems to be much closer to an incremental library (self-adjusting computations). Due to this I think it's actually a great idea by the folks working on this to not use common nomenclature, as it helps rid people of preconceptions about how things should work.

I think the best way to think about libraries like these are like comparing it to excel, where other values update as the new value is set and then propagates through the tree.

All 4 comments

I've had an almost opposite experience picking up this library, funny how things work like that.

I can't really speak to the memory leak questions, as I'm new to this just as you are.

This is my take though:

How do you unregister an atom from Recoil's state?

I'm not sure what you mean by this, are you wanting to reset the atom? You can do that by setting the atom to DefaultValue or calling the .reset method in the selector set function.

There is there an issue with hot-reloading where after reloading, the state setters don't force components to update. No clue what's going on here.

They mentioned that in another issue, they're aware of it, but it won't be fixed for a bit. I believe @davidmccabe is refactoring part of the library that will resolve this issue.

I'm getting a warning when using get in select's get prop function: "Warning: Cannot update a component from inside the function body of a different component". I'm assuming this is because I'm updating Recoil state in Component1 and Component2 is reading that value.

I've not run into this issue, but I can confirm that updating recoil state from one component and reading it in another isn't an issue.

how do I set a state based on a previous value using a selector?

I believe this can be done in two ways:

  • useState or useSetState accept a 'T -> 'T -> unit overload (compared to the normal 'T -> unit) that feeds in the current value.
  • The set method for selectors have an overload to feed in the previous value similar to thie above point.

I have general problems with the nomenclature

I'm not using this library natively as I'm writing bindings to use it from F#, so I'm able to change the naming a little bit, but for the most part I quite like it.

I do think most of these concerns will be resolved as the documentation is finished/polished.

It's a bit confusing having hooks that essentially do the same thing: useRecoilState, useSetRecoilState, useRecoilValue. I get the reasons behind having different ones, but it's too confusing to use your library for newcomers when they have to learn little intricacies like this.

I'm a big fan of being able to only have one component subscribe to the value of an atom and yet allow another to set the values. It's a great performance increase.

Most of the hooks follow a very familiar pattern via React, I personally found it quite easy to pick up and run with it.

Naming both derived states and atoms as somethingState in your examples is confusing because derived states only sometimes have a setter. If I was developing a larger application, I would have no clue which is which.

I definitely agree here, I made all values returned from atoms and selectors in my bindings as RecoilValue with type restrictions to determine if it's settable: RecoilValue<'T,ReadOnly> and RecoilValue<'T,ReadWrite>.

The get function in the callback of the get prop in a selector is technically the state selector function. I get if you have a set function, it makes sense to call it get, but it could be select and change or update. Doesn't have to be get and set.

I have mixed feelings on this point. get and set are very straightforward, but they don't quite describe what it's doing. This is mostly because they're either getting/setting, mapping, or binding the value.

Since Recoil is essentially the same as every other state library, I would expect to see names with people are familiar. I'm familiar with selector, getState, dispatch, and setState, but not necessarily atom and the way selector is used.

I think this may be part of why you're having some issues, I'd argue it's very much not like every other state library, as it seems to be much closer to an incremental library (self-adjusting computations). Due to this I think it's actually a great idea by the folks working on this to not use common nomenclature, as it helps rid people of preconceptions about how things should work.

I think the best way to think about libraries like these are like comparing it to excel, where other values update as the new value is set and then propagates through the tree.

Hi @Sawtaytoes.

How do you unregister an atom from Recoil's state? Isn't this a possible situation for a memory leak?
...
didn't realize selectors were storing literally every state update as a new memoized value. Isn't that a possible area for memory leaks?

Right now you can't unregister an Atom, and yes you do get a memory leak if you keep creating new states. It looks like there are plans to address these. Some details here: https://github.com/facebookexperimental/Recoil/issues/30#issuecomment-629388549 and https://github.com/facebookexperimental/Recoil/issues/56#issuecomment-630957954

I'm familiar with selector, getState, dispatch, and setState, but not necessarily atom and the way selector is used.

The term Atom is something that is used in other similar granular state management approaches. For example: https://clojure.org/reference/atoms.

Thanks for the detailed feedback! I really appreciate it. And thanks for your interest and making that livestream.

Let me go through each issue you raised:

How do you unregister an atom from Recoil's state? Isn't this a possible situation for a memory leak?

Automatic forgetting of atoms is on our near roadmap. In the meantime, you can use the useResetRecoilState hook to clear them manually.

There is there an issue with hot-reloading where after reloading, the state setters don't force components to update. No clue what's going on here.

This is a bug, we are working on the fix.

I'm getting a warning when using get in select's get prop function: "Warning: Cannot update a component from inside the function body of a different component". I'm assuming this is because I'm updating Recoil state in Component1 and Component2 is reading that value.

This warning was introduced in React just as we open-sourced Recoil unfortunately. This will go away when we release our CM-compatible strategy.

Memoization of selector values is causing strange issues. I was trying to calculate a new state in my derived state based on the previous state kinda like a reducer. Since one wasn't available, I went ahead and created a count variable outside the selector and whenever the get function caused the selector to reload, it would update the count. Sadly, because of memoization, the count changed in unexpected ways. So my real question is how do I set a state based on a previous value using a selector?

Every place that you can set an atom or selector value also accepts an updater form, just like setState.

I didn't realize selectors were storing literally every state update as a new memoized value. Isn't that a possible area for memory leaks?

This is customizable using the cacheImplementation property of selectors. We are also working on a higher-level interface for this.

It's a bit confusing having hooks that essentially do the same thing: useRecoilState, useSetRecoilState, useRecoilValue. I get the reasons behind having different ones, but it's too confusing to use your library for newcomers when they have to learn little intricacies like this.

I agree that there are a lot of hooks, but we haven't been able to pare them down beyond what we have. Each one serves a necessary purpose. At this point then it's a matter of well-structured documentation to reveal the complexity at the right pace.

Naming both derived states and atoms as somethingState in your examples is confusing because derived states only sometimes have a setter. If I was developing a larger application, I would have no clue which is which.

For documentation and teaching purposes this is probably true. As for actual use, having the ability to swap between state and derived without changing the public interface is a key benefit of Recoil.

The get function in the callback of the get prop in a selector is technically the state selector function. I get if you have a set function, it makes sense to call it get, but it could be select and change or update. Doesn't have to be get and set.

Yeah get => ({get}) definitely looks weird, is that what this is about? Unfortunately get is the most natural word for both of these functions, and after trying other alternatives we thought it was worth it.

Thanks everyone for your responses.

@davidmccabe
For the last one about get => ({get}), yeah, but about get => ({getState})? It's been a while so my memory's a bit fuzzy. Technically both are getState and setState though hmm... Yeah. I dunno, but that is the part I was referring.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jamiewinder picture jamiewinder  路  3Comments

polemius picture polemius  路  3Comments

ibnumusyaffa picture ibnumusyaffa  路  4Comments

yuantongkang picture yuantongkang  路  3Comments

atanasster picture atanasster  路  3Comments