Skip to main content

Recoil 0.2

· 4 min read

We are pleased to announce the release of Recoil 0.2.0. This release has a new, more reliable implementation of async selectors, greatly improved performance, and many bug fixes and improvements.

Better Async Selectors

We've reimplemented selectors to fix a number of corner cases that we didn't handle before. For instance:

  • Selectors can now add dependencies at any point in their async execution.
  • Selectors now start over if their dependencies change while they're awaiting something.
  • Diamond dependency patterns now re-execute only the necessary selectors.

These are just a few of the unit tests that pass only with the new implementation. You are now less likely to hit incorrect behavior when using async selectors in Recoil.

Many of the fixed bugs manifested as unnecessary execution of selectors. In one app that heavily uses async selectors, we observed an overall 15% decrease in the number of selector executions.

Christian Santos (@csantos42) has been working on this with great diligence and we're extremely happy to release it today.

Scaling to Large Numbers of Atoms

Recoil now uses special data structures to maintain efficiency with large numbers of atoms. It is now hundreds of times faster when using 10,000 atoms.

Recoil now has a logo. Thanks to Gray Pegg (@graypegg) for designing it and for sprucing up our website!

Other Improvements

  • Error handling with waitForAny() has changed: It now lets you access the individual Loadables corresponding to its arguments, even if one of them is in an error state. This makes it more consistent with waitForNone().
  • Added a waitForAllSettled() helper analogous to Promise.allSettled.
  • Friendlier error message for misuse of useRecoilCallback(). (#870)
  • Friendlier error message if you try to use an async function as a selector setter, which is not supported. (#777)
  • Improved React Native support. (#748, #702)
  • Added a useGetRecoilValueInfo_UNSTABLE() hook for use by dev tools. (#713, #714)

Bug Fixes

  • Selectors now treat any non-Promise that is thrown as an error, rather than only instances of Error.
  • <RecoilRoot> could sometimes have its state updated after being unmounted. (#917)
  • The error message for a missing <RecoilRoot> wasn't displayed on React experimental releases. (#712)
  • Errors are no longer frozen. (#852)
  • Atom effects could fail to initialize atoms in certain cases (#775).
  • Async selectors would fail with multiple React roots.
  • IE 11 compatibility (#894 and more)

Breaking Changes

As mentioned above, the behavior of waitForAny() has changed. If you rely on the behavior that waitForAny() resulted in an error state if any of its arguments is in an error state, your code will break. Now it returns individual Loadables for each argument, just as with the non-error state, and you can check for an error in each argument.

If you throw something that is not a Promise and not an Error from within a selector, it will now put the selector into an error state instead of treating the thrown object as a value.

Future Work

We are very close to releasing Memory Management, which will allow Recoil to automatically delete atoms and selectors that are no longer being used, configurable on a per-atom basis. This will also include configurable LRU caching for selectors. These new features are in use internally at Facebook, but require a slight breaking change to the API. So our next release will start warning in cases where your code will break. The required changes are not difficult, and should be rare: Out of thousands of modules that import Recoil, we found less than half a dozen that required changes.