Skip to main content

Recoil 0.4.1

· One min read

Recoil 0.4.1 has been released with some performance optimizations and fixes focused on optimizing when React will re-render components based on Recoil state changes.

  • Components using selectors will no longer re-render if the selector evaluates to an equivalent value, based on reference equality.
  • Components previously rendered twice on initial render when not using React's Concurrent Mode.
  • When selectors have async dependencies that resolve, some environments of React that previously caused unnecessary re-renders no longer will.
  • Components did not always re-render properly when selectors changed async dependencies based on other dependencies.

Recoil 0.4

· 4 min read

We are pleased to announce the release of Recoil 0.4 with configurable selector caches, improved API for transactions with multiple atoms, and other optimizations and fixes.

Configurable selector caches#

The new cachePolicy_UNSTABLE property in selectors and selector families allows you to configure the caching behavior of a selector's internal cache. This property can be useful for reducing memory in applications that have a large number of selectors or selectors that have a large number of changing dependencies.

Below is an example of how you might use this new property:

const clockState = selector({  key: 'clockState',  get: ({get}) => {    const hour = get(hourState);    const minute = get(minuteState);    const second = get(secondState); // will re-run every second
    return `${hour}:${minute}:${second}`;  },  cachePolicy_UNSTABLE: {    // Only store the most recent set of dependencies and their values    eviction: 'most-recent',  },});

In the example above, clockState recalculates every second, adding a new set of dependency values to the internal cache, which may lead to a memory issue over time as the internal cache grows indefinitely. Using the most-recent eviction policy, the internal selector cache will only retain the most recent set of dependencies and their values, along with the actual selector value based on those dependencies, thus solving the memory issue.

Current eviction options are:

  • lru - evicts the least-recently-used value from the cache when the size exceeds maxSize.
  • most-recent - retains only the most recent value.
  • keep-all (default) - keeps all entries in the cache and does not evict.

NOTE: The default eviction policy (currently keep-all) may change in the future.

Transactions with multiple atoms#

Introducing an improved API for updating multiple atoms together as a single transaction. The new useRecoilTransaction_UNSTABLE() hook is easier, more efficient, and safer than before. This new hook should eventually replace most uses of useRecoilCallback(), however this release is only an initial implementation with certain limitations that will be addressed in future releases.

Example#

Suppose we have two atoms, positionState and headingState, and we'd like to update them together as part of a single action, where the new value of positionState is a function of both the current value of positionState and headingState. You can accomplish this with a transaction, which must be a pure function without side-effects:

const goForward = useRecoilTransaction_UNSTABLE(({get, set}) => (distance) => {  const heading = get(headingState);  const position = get(positionState);  set(positionAtom, {    x: position.x + cos(heading) * distance,    y: position.y + sin(heading) * distance,  });});

Then you can execute the transaction by just calling goForward(distance) in an event handler. This will update state based on the current values, not the state when the components rendered. You can also read the values of previous writes during a transaction. Because no other updates will be committed while the updater is executing, you will see a consistent store of state.

the previous approach using useRecoilCallback() might have looked like the following:

const goForward = useRecoilCallback(({snapshot, gotoSnapshot}) => (distance) => {  const mutatedSnapshot = snapshot.map(({get, set}) => {    const heading = get(headingState);    const position = get(positionState);    set(positionState, {      x: position.x + cos(heading) * distance,      y: position.y + sin(heading) * distance,    });  });  gotoSnapshot(mutatedSnapshot);});

This has the following drawbacks:

  • There is performance overhead for managing the full generality of snapshots.
  • There is more opportunity for bugs: The snapshot might be retained and used in the future. Since a snapshot contains the complete set of Recoil state, not just a changeset, that could accidentally rewind changes that occurred between creating and committing the snapshot.

Reducer Example#

You can also use this hook to create a reducer pattern of executing actions over multiple atoms:

const reducer = useRecoilTransaction_UNSTABLE(({get, set}) => action => {  switch(action.type) {    case 'goForward':      const heading = get(headingState);      set(positionState, position => {        x: position.x + cos(heading) * action.distance,        y: position.y + sin(heading) * action.distance,      });      break;
    case 'turn':      set(headingState, action.heading);      break;  }});

Fixes and Optimizations#

  • Fix TypeScript typing for selectorFamily(), getCallback(), useGetRecoilValueInfo(), and Snapshot#getNodes() (#1060, #1116, #1123)
  • Allow mutable values in selectors (enabled via the dangerouslyAllowMutability selector option) to be used with waitFor*() helpers such as waitForAll() (#1074, #1096)
  • Atom Effects fixes:
    • Fix onSet() handler to get the proper new value when an atom is reset or has an async default Promise that resolves (#1059, #1050, #738) (This is a slightly breaking change because the actual new value will be provided to the handler instead of a DefaultValue placeholder)
    • Fix support for multiple Atom Effects cleanup handlers (#1125)
    • Fix selector subscriptions when atoms with effects are initialized via a Snapshot (#1135, #1107)
  • Optimization for async selectors when dependencies resolve to cached values (#1037)
  • Remove unnecessary warning message (#1034, #1062)

Recoil 0.3

· One min read

我们很高兴地宣布 Recoil 0.3 的发布,它具有更灵活的 RecoilRoot 嵌套,回调生成,内存管理的准备、优化及错误修复。

Recoil 0.2

· One min read

Recoil 0.2.0 发布啦!这个版本有一个新的、更可靠的异步 selector 的实现,大大提高了性能,并有许多错误修复和改进。

Recoil 0.1.1

· 3 min read

Welcome to Recoil version 0.1.1! This release has performance improvements and introduces several experimental features such as support for React Native, working with Snapshots outside of React, and atom effects for managing side-effects of atoms such as logging, synchronization with external stores, persistence, etc.

Recoil 0.0.11

· 6 min read

Today we are releasing Recoil 0.0.11. It contains bug fixes, new features, better performance, and experimental compatibility with Concurrent Mode. Thank you so much to everyone who contributed to this release!

Recoil 0.0.10

· 8 min read

Recoil 0.0.9 and 0.0.10 is being released with some bug fixes, TypeScript support, and a new API for Recoil Snapshots to observe, inspect, and manage global Recoil atom state. Thanks again to everyone who helped make this possible and stay tuned for more exciting developments coming soon!

Recoil 0.0.8

· 4 min read

Today we are releasing Recoil 0.0.8. It contains bug fixes and new features. Thanks so much to everyone who contributed to this release! It's been amazing to see so many people contribute.