Recoil: Can an atom's value be changed from a non-react environment?

Created on 2 Sep 2020  路  5Comments  路  Source: facebookexperimental/Recoil

Recently I came across the use case like a pure js file Realtime.service.js is making a connection with the WebSocket server and streaming realtime data to UI (need to feed multiple React's components with a chunk of data coming from realtime WS).

Now if Recoil's atom state can be changed directly from Realtime.service.js (which is not a React component but js service) with realtime steam then all the components connected with the relative atoms will be updated on realtime data streaming.

Is that a valid use case? Right now I've seen that Atom's state only get/set default-value by React's hook.

In redux it is possible, we can update the store from any js file (non-react env). and the react-redux bridge auto handles the changes to connected components. Now we're moving from Redux to Recoil, we're having this roadblock.

question

Most helpful comment

I'm hopeful that this API #380 could help with this use case.

All 5 comments

It isn't possible to access them outside of React as stated in #5. But you can do something like this to sync them:
https://recoiljs.org/docs/guides/asynchronous-state-sync

I use this method to sync my state to leveldown DB in my electron application.

You could hack it

function MyCmp () {
  const setValue = window.setValue = useSetRecoilState(someState)
}

function somewhereElse () {
  window.setValue(123)
}

My team uses an empty component to capture and expose a getLoadable() and set() function. This only works if you have exactly one <RecoilRoot> though:

import React from 'react';
import {
    atom, AtomOptions, Loadable, RecoilState, RecoilValue, selector, Snapshot, useRecoilCallback,
    useRecoilSnapshot
} from 'recoil';

// internal variables to hold the relevant recoil objects
let __snapshot: Snapshot = null as any;
let __set: <T>(
  recoilVal: RecoilState<T>,
  valOrUpdater: ((currVal: T) => T) | T
) => void = null as any;

// expose a function to set recoil state
export function RecoilSet<T>(
  recoilVal: RecoilState<T>,
  valOrUpdater: ((currVal: T) => T) | T
) {
  __set(recoilVal, valOrUpdater);
}

// expose a function to get recoil state
export function RecoilGetLoadable<T>(recoilValue: RecoilValue<T>): Loadable<T> {
  return __snapshot.getLoadable(recoilValue);
}

// This component captures a snapshot to expose a get function, and a callback to expose a set function
export function RecoilUtilsComponent() {
  __snapshot = useRecoilSnapshot();

  useRecoilCallback(({ set }) => {
    __set = set;

    return async () => {};
  })();

  return <></>;
}

And this is our react root:

export default function Startup() {
  return (
    <RecoilRoot>
      {/* this component is empty and it's only purpose is to sync external state with Recoil */}
      <RecoilUtilsComponent />
      <HistoryRecoilSync />
      <MediaQueryRecoilSync />
      <Initialize>
        <AppHost />
      </Initialize>
    </RecoilRoot>
  );
}

It's handy when we want to set some auth data as part of authentication/login flow.

I'm hopeful that this API #380 could help with this use case.

Updated documentation for syncing Recoil state with external storage in #680

Was this page helpful?
0 / 5 - 0 ratings

Related issues

thegauravthakur picture thegauravthakur  路  3Comments

jamiewinder picture jamiewinder  路  3Comments

karevn picture karevn  路  3Comments

jamiebuilds picture jamiebuilds  路  3Comments

Sawtaytoes picture Sawtaytoes  路  4Comments